我试图让Typescript在switch case中推断正确的对象,我很难理解为什么我的场景不工作(与对象),但当它是联合类型时却能按预期工作。
从操场链接:
interface Base {
id: number;
name: "a" | "b" | "c" | "d"; // This is type-guarding correctly
type: {
id: number;
name: "a" | "b" | "c" | "d"; // This is NOT type-guarding correctly
}
}
interface A extends Base {
name: "a";
type: {
id: number;
name: "a";
}
a: {
b: number;
}
}
interface B extends Base {
name: "b";
type: {
id: number;
name: "b";
}
b: {
a: number;
}
}
interface Generic extends Base {
name: "c" | "d";
type: {
id: number;
name: "c" | "d";
}
}
type C = A | B | Generic
function test(t: C) {
// If you switch the switch case to 't.name'
switch(t.type.name) {
// switch(t.name) {
case "a":
// This will be properly type-guarded as it will infer A
console.log(t.a)
break;
case "c":
console.log(t.name);
break;
case "b":
// This will be properly type-guarded as it will infer B
console.log(t.b);
break;
}
}
我有一个对象,它可以根据type.name
的值具有不同的属性。我希望在switch case中检查该type.name
,并让typescript了解我可以基于此访问哪些属性。根据示例,如果我在name
上执行switch case,它将按预期工作。
Playground链接
除了使用as
或平面化对象之外,我还有哪些其他选项?
1条答案
按热度按时间dsf9zpds1#
TypeScript不支持“嵌套”区分联合体,在这种情况下,您使用子属性来区分成员。请参见microsoft/TypeScript#18758。因此,虽然
C
* 是 * 一个以name
作为判别式的区分联合体,但您不能使用type.name
作为判别式。因此,您几乎不能使用任何设计用于处理区分联合体的机制。例如在判别特性上使用switch
。如果你想让编译器对子属性进行类型保护,这样你就不必使用类型Assert或改变你的对象结构,那么我能想到的唯一选择就是编写一个用户定义的类型保护函数来为你执行收缩。它可以看起来像这样:
如果你调用
hasNestedTypeName(t, k)
,它返回true
,那么编译器会将t
的范围缩小到type.name
属性可以接受k
作为值的联合成员。else
链而不是switch
,因为boolean
返回类型保护函数的类型。如下所示:这和你的版本差不多(实际上很难让
hasNestedTypeName(t, "c")
按预期工作,因为如果它返回false
,编译器会假设t
* 不是 *Generic
,但这不一定。我会远离判别式本身就是一个并集的判别式并集,因为它可能会做一些奇怪的事情)。是否值得这样的改变取决于用例。我个人建议重构对象形状,使判别式处于顶级是最好的方法,因为它利用了语言的优点,而不是试图绕过它的缺点。
Playground代码链接