【问题标题】:How to enforce mapped type to have all of the keys in a string literal如何强制映射类型具有字符串文字中的所有键
【发布时间】:2022-01-09 19:20:57
【问题描述】:

给定以下列表:

const list = ["A", "B", "C"] as const;
type List = typeof list[number];

我有一张必须包含list 的所有可能键的地图:

const mapping: Record<List, unknown> = {
  A: true,
  B: 2,
  C: "three"
};

就像我可以强制 mapping 映射到 List 一样,我想对类型做同样的事情。像这样的东西(我知道这是一个无效的语法):

type MappedList: Record<List, unknown> = {
  A: boolean,
  B: number,
  C: string
}

我的主要目标是防止出现我将新单元格添加到 list 并忘记将其添加到 MappedList 的情况。

See playground

【问题讨论】:

  • 您是否绝对有必要在数组中拥有所需键的列表?我可以提出一个真实值是对象而不是数组的解决方案吗?

标签: typescript


【解决方案1】:

AFAIK,没有类型对类型的概念。但是,您可以使用 mapped types 从另一种类型创建一种类型。

const list = ["A", "B", "C"] as const;

type ListKey = typeof list[number];

// type MappedList = {
//     A: "property";
//     B: "property";
//     C: "property";
// }
type MappedList = {
  [Prop in ListKey]: 'property'
}

据我了解,您还需要确保 AbooleanBnumberCstring。为此,您需要创建一个映射和条件类型:

const list = ["A", "B", "C"] as const;

type ListKey = typeof list[number];

type TypeMap = {
  A: boolean,
  B: number,
  C: string
};

/**
 * If T is a subtype of TypeMap
 * and keyof T extends keyof TypeMap
 */
type BuildMappedList<T> = T extends TypeMap ? keyof T extends keyof TypeMap ? T : never : never;

/**
 * Ok
 */
type MappedList = BuildMappedList<{
  A: true,
  B: 2,
  C: "three",
}>

/**
 * Never
 */
type MappedList2 = BuildMappedList<{
  A: true,
  B: 2,
  C: "three",
  D: [2] // because of extra D property
}>

/**
 * Never
 */
type MappedList3 = BuildMappedList<{
  B: 2,
  C: "three",
}> // because no A property

/**
 * Never
 */
type MappedList4 = BuildMappedList<{
  A: false,
  B: [2], // because B is not a number
  C: "three",
}> 

enter link description here

【讨论】:

  • 在泛型实体中,类型参数的约束类似于“类型的类型”,——我们可以利用这个想法吗?
  • @DimaParzhitsky 当然可以。我只是说没有办法使用: 表示法。
  • 虽然MappedListX 会被认为是never,但我们不会在这种类型上真正得到任何错误,而是在依赖它的代码上,所以它是部分可以的。虽然它可能对其他人有帮助,但另一个答案符合我的需要。谢谢!
  • @EliyaCohen 当然,谢谢您的反馈
【解决方案2】:

您可以通过创建一个需要泛型匹配的实用程序来做到这一点:

type AssertKeysEqual<
  T1 extends Record<keyof T2, any>,
  T2 extends Record<keyof T1, any>
> = T2


const list = ["A", "B", "C"] as const;
type ListKey = typeof list[number];

const mapping: Record<ListKey, unknown> = {
  A: true,
  B: 2,
  C: "three",
};

type MappedList = AssertKeysEqual<Record<ListKey, unknown>, {
  A: boolean;
  B: number;
  C: string;
}>

Typescript playground

【讨论】:

  • 我会将any 替换为unknown,但这似乎是满足我需求的有效解决方案。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-12
  • 2021-06-19
  • 1970-01-01
相关资源
最近更新 更多