一般来说,TypeScript 不会执行从属性向上传播联合的类型。也就是说,虽然该类型的每个值{foo: string | number}
应该可以分配给类型{foo: string} | {foo: number}
,编译器不会将它们视为可相互分配的:
declare let unionProp: { foo: string | number };
const unionTop: { foo: string } | { foo: number } = unionProp; // error!
除了当您开始改变此类类型的属性时发生的奇怪事情之外,对于编译器来说,始终这样做的工作量太大,尤其是当您有多个联合属性时。 Amicrosoft/TypeScript#12052 中的相关评论 https://github.com/microsoft/TypeScript/issues/12052#issuecomment-258653766 says:
这种等价仅适用于具有单一属性的类型,并且在一般情况下并不成立。例如,考虑{ x: "foo" | "bar", y: string | number }
相当于{ x: "foo", y: string } | { x: "bar", y: number }
因为第一种形式允许所有四种组合,而第二种形式只允许两种特定的组合。
因此,这本身并不是一个错误,而是一个限制:编译器只会花费大量时间来处理联合来查看某些代码是否有效。
在 TypeScript 3.5 中,添加了支持 http://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-5.html#smarter-union-type-checking专门针对以下情况进行上述计算受歧视的工会 http://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions。如果您的联合具有可用于区分联合成员的属性(这意味着单例类型,例如字符串文字 http://www.typescriptlang.org/docs/handbook/advanced-types.html#string-literal-types, 数字文字 http://www.typescriptlang.org/docs/handbook/advanced-types.html#numeric-literal-types, undefined
, or null
) in 至少有一些工会成员 http://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html#non-unit-types-as-union-discriminants,然后编译器将按照您想要的方式验证您的代码。
这就是为什么如果你改变它就会突然起作用T1A | T2A | T1B | T2B
只是T1A | T2A
,并且可以是您问题的可能答案:
function hello(type: 'type1' | 'type2'): T1A | T2A | T1B | T2B {
const x: T1A | T2A = { type, index: 3 }; // okay
return x;
}
后者是一个受歧视的工会:type
属性会告诉您您拥有哪个成员。但前者不是:type
属性可以区分T1A | T1B
and T2A | T2B
,但没有属性可以进一步细分。不,仅仅因为缺席index
or name
类型中的值不算作判别式,因为类型的值T1A
may have a name
财产; TypeScript 中的类型不是exact https://github.com/microsoft/TypeScript/issues/12936.
上面的代码之所以有效,是因为编译器可以验证x
属于类型T1A | T2A
,然后可以验证T1A | T2A
是完整的子类型T1A | T2A | T1B | T2B
。因此,如果您对两步流程感到满意,那么这对您来说是一个可能的解决方案。
如果你想T1A | T2A | T1B | T2B
要成为一个可区分的联合,您需要修改组成类型的定义,以便通过至少一个公共属性来真正区分。就像这样说:
interface T1A {
type: 'type1';
index: number;
name?: never;
}
interface T1B {
type: 'type1';
name: string;
index?: never;
}
interface T2A {
type: 'type2';
index: number;
name?: never;
}
interface T2B {
type: 'type2';
name: number;
index?: never;
}
通过添加类型的那些可选属性never
,您现在可以使用type
, name
, and index
作为判别式。这name
and index
属性有时会是undefined
,支持区分类型的单例类型。然后这有效:
function hello(type: 'type1' | 'type2'): T1A | T2A | T1B | T2B {
return { type, index: 3 }; // okay
}
所以这是两个选择。在您知道某些东西是安全的但编译器不知道的情况下,始终可用的另一个选项是使用类型断言 https://www.typescriptlang.org/docs/handbook/basic-types.html#type-assertions:
function hello2(type: 'type1' | 'type2') {
return { type, index: 3 } as T1A | T2A | T1B | T2B
}
好的,希望有帮助;祝你好运!
Playground 代码链接 http://www.typescriptlang.org/play/#code/CYUwxgNghgTiAEEQBd4FcB2BLA9hgCjDgA4Bc8A3vAGY47kDOyMWGA5vAD7wZoC2AIxAx4AXwDcAKDB4m6bHgAqJclVr14TFuzFdKNOuV6DhugLzzcBIsXHwA9PfjCiMAIST4kjFD4gGxFBgCAAiWAxgLHyUnvBxrMjC1EEIigCMAIIxcTk5yACexCDkAOQFRWklUrm5rKAAHkb8QjDVOaKSsfEYiTDJwfDpAELZNXHlxfBlhSCVbWM+fozMrGzzYp21PUkpgwBMWRRdNROlE3tVx1sNTSatxx3HCTsDinsjR2PjM2czF+u5RaTYwtdaPXLUTBgZBWeAACxAEAgOAAFKcphNKnppkULgBKcjpLLcN7EwZpEYk96jMYyDByRrksmk+AWKgTAA08DqIEZAGYxACcnBkGgYBh4PUwZscpCMNDYQikTg9mifhiZljuDiQPiaTURWKJeyZlyefzdFAGEy9CySRTbe8Hp1HkCArsALJQADWIDCESi+uefV2RP1uXROrmVxy5tuoJjcSBAH4jCAAG7CaVPbYh14Oz5fSOYy5fHi+SZaVZC7oNVM8DNZ5053r9VIHcN5dU6-6J7kYG48ZpNssptOZ+65cGx3Nt-YfPvFv6lr5A+Mjr7m+sYRuT9oyuJyhV4eGI5Fqoq-ColbHnEoEm1U5kOqkLsuG8X6Tn9wcCiTNx4gA