我有一个简化的例子。我创建了一个有区别的unionAOrB
,并且(在我看来)创建了一个兼容类型C
:
interface A {
kind: 'a';
}
interface B {
kind: 'b';
}
type Kind = 'a' | 'b';
interface C {
kind: Kind;
}
interface D {
kind: 'b';
}
type AOrB = A | B;
function test(input: C): AOrB {
return input;
}
function test2(input: D): AOrB {
return input;
}
失败的原因
Type 'C' is not assignable to type 'AOrB'.
Type 'C' is not assignable to type 'B'.
Types of property 'kind' are incompatible.
Type 'Kind' is not assignable to type '"b"'.
Type '"a"' is not assignable to type '"b"'.
这使得我似乎不能将对象分配给一个类型
AOrB
的变量,除非它提前知道它究竟是事实上还是A
还是B
。不仅仅是他们需要相同的精确类型,如test2
编译好。有人能解释一下发生了什么事吗?
最佳答案
更新:2019-05-30随着Typescript 3.5的发布,这应该通过smarter union type checking来解决。以下适用于3.4及以下:
这个问题以前是AA>,它本质上是编译器的限制。虽然您和我都明白,cc相当于{a: X} | {a: Y}
,但编译器没有。
一般来说,除非对象不同于一个属性的类型(或者不同的属性表示联盟的所有可能分布),否则不能将对象的联合合并为对象的对象。例如,不能将{a: X | Y}
折叠成类似于{a: X, b: Z} | {a: Y, b: W}
的内容…类型{a: X | Y, b: Z | W}
可分配给后者,但不能分配给前者。
想象一下,编写编译器尝试进行这样的裁减;对于绝大多数情况,它会花费宝贵的处理器时间来检查联合类型,只发现它不能组合它们。像你这样比较罕见的情况下,减少可能只是不值得。
即使是在你的情况下,我也持怀疑态度;你真的在试图建立一个只有歧视性质不同的歧视联盟吗?那是个病理病例。区分联合的预期用例是获取一组不同的类型并添加一个属性,以让您知道值是哪种类型。一旦您开始添加其他使受歧视类型真正不同的属性,您就必须放弃折叠联合的想法。
所以,假设你真的需要上面的非歧视联合,你能做什么?最简单的方法是使用has been reported告诉编译器您知道自己在做什么:
function test(input: C): AOrB {
return input as AOrB; // you know best
}
现在可以编译了,尽管它并不真正安全:
function test(input: C): AOrB {
return (Math.random() < 0.5 ? input : "whoopsie") as AOrB; // also compiles
}
一个更安全更复杂的解决方案是让编译器了解不同的可能性:
function test(input: C): AOrB {
return input.kind === 'a' ? {kind: input.kind} : {kind: input.kind};
}
不管是这些还是其他你想出来的东西都应该有用。
好吧,希望能帮上忙;祝你好运!