我想编写一个通用的创建方法,该方法生成在 map 内键入的不同对象。问题是 typescript 将所有接口(interface)混合在一起,而不是只在接口(interface)映射中选择一个:
interface A {
a: string;
}
interface B {
b: string;
}
interface ABMap {
a: A;
b: B;
}
function create<ID extends keyof ABMap>(id: ID): ABMap[ID] { // this is now combined A & B instead of A | B
if (id === 'a') {
return {a: 'a'}; // error a is missing b key
} else if (id === 'b') {
return {b: 'b'};
}
}
最佳答案
这里有两个问题。
首先,typescript 不会基于类型保护来缩小泛型的定义。这是一种已知和预期的行为,尽管它可能令人沮丧。当您查看 if (id === 'a')
, typescript 知道变量 id
的值是 A
类型.然而,它并没有将泛型缩小到仅 A
.它对泛型类型完全有效 ID
成为联盟A | B
和变量 id
成为 A
.因此,当检查通过时,我们知道 ID
必须包含 A
,但我们不知道它是“A
并且只有 A
”。
第二个问题是为什么类型 ABMap[ID]
正在缩小到 A & B
而不是 A | B
.那个我无法解释。
有(至少)三种解决方案。
ABMap[keyof ABMap]
又名 A | B
function create(id: 'a'): ABMap['a']
function create(id: 'b'): ABMap['b']
function create(id: keyof ABMap): ABMap[keyof ABMap] {
if (id === 'a') {
return {a: 'a'};
} else {
return {b: 'b'};
}
}
as
保留您的泛型并告诉 typescript 您返回的实际上是正确的类型。关键词。 function create<ID extends keyof ABMap>(id: ID): ABMap[ID] {
if (id === 'a') {
return {a: 'a'} as ABMap[ID];
} else {
return {b: 'b'} as ABMap[ID];
}
}
Playground Link
关于typescript - 根据映射键从接口(interface)映射中选择类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64447785/