这就是 TS 工会的工作方式。这是设计使然。
type Option1 = {
items: string[];
}
type Option2 = {
delete: true;
}
type Combined = Option1 | Option2;
type Keys = keyof Combined; // never
如您所见,keyof Combined 返回 never - 空键集。
因为Option1和Option2没有任何共同的props,TS不确定允许什么属性。
假设您有一个需要Option1 或Option2 的函数。为了安全地使用Combined,您应该使用自定义typeguards:
type Option1 = {
items: string[];
}
type Option2 = {
delete: true;
}
type Combined = Option1 | Option2;
type Keys = keyof Combined; // never
const hasProperty = <Obj, Prop extends string>(obj: Obj, prop: Prop)
: obj is Obj & Record<Prop, unknown> =>
Object.prototype.hasOwnProperty.call(obj, prop);
const handle = (union: Combined) => {
if (hasProperty(union, 'items')) {
const option = union; // Option1
} else {
const option = union; // Option2
}
}
或者你可以添加公共属性:
type Option1 = {
tag: '1',
items: string[];
}
type Option2 = {
tag: '2',
delete: true;
}
type Combined = Option1 | Option2;
type CommonProperty = Combined['tag'] // "1" | "2"
const handle = (union: Combined) => {
if(union.tag==='1'){
const option = union // Option1
}
}
另一种替代方法是使用StrictUnion。
type Option1 = {
items: string[];
}
type Option2 = {
delete: true;
}
type Combined = Option1 | Option2;
// credits goes https://stackoverflow.com/questions/65805600/type-union-not-checking-for-excess-properties#answer-65805753
type UnionKeys<T> = T extends T ? keyof T : never;
type StrictUnionHelper<T, TAll> =
T extends any
? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, never>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>
type Union = StrictUnion<Combined>
const items_variable: Union['items'] = ["a", "b"]; // string[] | undefined
您可能已经注意到,有一个小缺点,items_variable 也可能是undefined。这是因为工会的性质。它可以是一个值,也可以是另一个值。