Typescript:基于类型的鉴别联合



为什么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。这就是说,类型AorB期望有一个对象,其中inner属性为已知并固定为A1B1

但等待吗?既然封闭对象的属性是相同的(即foobar),上面的类型不应该等价于:

{
foo: number;
bar: string;
inner: A1 | B1;
}

从逻辑上讲,这是有道理的,您可以将内部联合分布在封闭对象类型上,并看到您将获得与AorB相同的对象联合。事实上,这似乎是一个已知的问题,但目前TypeScript还不能做出这样的推断。


为了解决这个问题,我看到了一些选项。首先,您可以为AorB使用单个接口,其中inner属性的类型为A1 | B1:

interface AorB {
foo: number;
bar: string;
inner: A1 | B1;
}

作为另一个选项,您可以修改在test函数中构造返回对象的方式,以便向typescript清楚地表明结果对象具有固定的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
};
}

相关内容

  • 没有找到相关文章

最新更新