隐式打字稿按键映射使用

问题描述 投票:0回答:1

是否可以在下面的示例中隐式说明

key
属性的可能选项(基于
name
):

type TypeMap = {
  A: 1 | 2 | 3;
  B: 4 | 5 | 6;
};

type InnerType<TKey extends keyof TypeMap> = {
  key: TKey;
  name: TypeMap[TKey];
  // + other properties;
};

type OuterType = {
  x: InnerType; // Should be agnostic at this point
  y: InnerType; // but typescript requires a generic definition
};

const foo: OuterType = {
  x: { key: "A", name: 2 },
  y: { key: "B", name: 4 },
};

我能想到的最接近的是如下所示,(但我宁愿不必更改相应类型的格式):

type TypeMap = {
  A: 1 | 2 | 3;
  B: 4 | 5 | 6;
};

type InnerType = {
  data: Partial<{ [key in keyof TypeMap]: TypeMap[key] }>;
  // + other properties
};

type OuterType = {
  x: InnerType;
  y: InnerType;
};

const foo: OuterType = {
  x: { data: { A: 2 } }, // not blocking me from using `{}` or `{ A: 2, B: 4 }` on the same object
  y: { data: { B: 4 } },
};

另一种方法如下(但不基于密钥进行限制):

type TypeMap = {
  A: 1 | 2 | 3;
  B: 4 | 5 | 6;
};

type InnerType = {
  key: keyof TypeMap;
  name: TypeMap[keyof TypeMap];
  // + other properties;
};

type OuterType = {
  x: InnerType;
  y: InnerType;
};

const foo: OuterType = {
  x: { key: "A", name: 2 }, // allows me to use `{ key: "A", name 4 }`
  y: { key: "B", name: 4 },
};
typescript generics keyof
1个回答
0
投票

您的

InnerType
版本是单个对象类型,其属性为 unions,它允许以您不希望的方式混合
key
name
。您确实希望
InnerType
本身成为对象类型的联合,如下所示:

type InnerType = {
    key: "A";
    name: 1 | 2 | 3;
} | {
    key: "B";
    name: 4 | 5 | 6;
}

这给了你你想要的行为:

const foo: OuterType = {
    x: { key: "A", name: 1 },
    y: { key: "B", name: 5 }
};

const badFoo: OuterType = {
    x: { key: "A", name: 4 }, // error! 
    // Type '{ key: "A"; name: 4; }' is not assignable to type 'InnerType'.
    // Type '4' is not assignable to type '1 | 2 | 3'.
    y: { key: "A", name: 2 }
}

您甚至可以用

InnerType
来写
TypeMap
,这样当
TypeMap
更新时它会自动更新:

type InnerType = { [K in keyof TypeMap]:
    { key: K, name: TypeMap[K] }
}[keyof TypeMap]

这个

InnerType
是在microsoft/TypeScript#47109中创造的分布式对象类型。它的工作原理是映射K
的键
TypeMap
,计算每个键
K
所需的对象类型,然后使用
keyof TypeMap索引到
映射类型,从而得到所需的并集。


如果出于某种原因您想保留

InnerType

 
generic 以便 InnerType<K>
 对应于特定的 
K
 
constrainedkeyof TypeMap
,您可以这样做:

type InnerType<K extends keyof TypeMap = keyof TypeMap> = { [P in K]: { key: P, name: TypeMap[P] } }[K] type InnerTypeA = InnerType<"A"> /* type InnerTypeA = { key: "A"; name: 1 | 2 | 3; } */ type InnerTypeB = InnerType<"B"> /* type InnerTypeB = { key: "B"; name: 4 | 5 | 6; } */
并注意 

K

 
defaultskeyof TypeMap
,因此没有泛型类型参数的 
InnerType
 相当于完整的联合:

type InnerTypeBoth = InnerType /* type InnerTypeBoth = { key: "A"; name: 1 | 2 | 3; } | { key: "B"; name: 4 | 5 | 6; } */
因此,您可以根据您的需要获得类似泛型和类似联合的行为。

Playground 代码链接

© www.soinside.com 2019 - 2024. All rights reserved.