[英]Typescript: Discriminated union based on type
为什么以下对于 TS 是不可能的? 为什么我不能使用类型作为判别式?
export interface A1 {
plop: number;
}
export interface B1 {
hop: number;
}
export interface A {
foo: number;
bar: string;
inner: A1;
}
export interface B {
foo: number;
bar: string;
inner: B1;
}
export type AorB = A | B;
function test(): AorB {
let inner: A1 | B1;
if (Math.random()) {
inner = {plop: 4};
} else {
inner = {hop: 43};
}
return {
foo: 42,
bar: 'plop',
inner
};
}
TS 编译器告诉我:
Type '{ foo: number; bar: string; inner: A1 | B1; }' is not assignable to type 'B'.
Types of property 'inner' are incompatible.
Type 'A1 | B1' is not assignable to type 'B1'.
Property 'hop' is missing in type 'A1' but required in type 'B1'.
考虑一下你的类型的扩展。 AorB
类型表示联合:
{
foo: number;
bar: string;
inner: A1;
} | {
foo: number;
bar: string;
inner: B1;
}
请注意, A1 | B1
类型在此没有任何地方。 A1 | B1
出现。 也就是说,类型AorB
期望有一个对象,其中inner
属性已知并固定为A1
或B1
。
可是等等? 由于封闭对象的属性是相同的(即foo
和bar
),所以上面的类型不应该等同于:
{
foo: number;
bar: string;
inner: A1 | B1;
}
从逻辑上讲,这是有道理的,您可以将内部联合分布在封闭对象类型上,并看到您将获得与AorB
相同的对象联合。 事实上,这看起来像是一个已知问题,但目前 TypeScript 无法做出这种推断。
为了解决这个问题,我看到了一些选项。 首先,您可以只为AorB
使用单个接口,其中inner
属性的类型为A1 | B1
A1 | B1
:
interface AorB {
foo: number;
bar: string;
inner: A1 | B1;
}
作为另一种选择,您可以修改在test
函数中构造返回对象的方式,以向打字稿清楚地表明结果对象具有固定的inner
属性:
function test2(): AorB {
let outer = {
foo: 42,
bar: 'plop',
};
if (Math.random()) {
// Clearly has type A
return {
...outer,
inner: {plop: 4}
}
} else {
// Clearly has type B
return {
...outer,
inner: {hop: 43}
}
}
}
这样,对于 TS 编译器来说,很明显一个分支返回类型A
和另一个类型B
,它们完全匹配AorB
的返回类型。
最后,因为我们人类可以看到这两种类型实际上是等价的,所以你总是可以忽略这个错误:
function test(): AorB {
let inner: A1 | B1;
if (Math.random()) {
inner = {plop: 4};
} else {
inner = {hop: 43};
}
// @ts-ignore: This is equivalent to the type AorB after distributing the inner union...
return {
foo: 42,
bar: 'plop',
inner
};
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.