TypeScript是否支持对象类型之间的真正XOR并集



我试图为一个对象定义一个类型,该对象要么是一组道具,要么是(同一组道具与所有其他道具)。特别是,我很难理解为什么TypeScript认为props2的值是有效的,因为它与类型并集的两边都不匹配:

type CommonProps = {a:number, b: number};
type OptionalProps = {c: number, d: number};
type Props = CommonProps | (CommonProps & OptionalProps)
const props1: Props = {a: 1, b: 2};                   // Should be valid and is
const props2: Props = {a: 1, b: 2, c: 3};             // Shouldn't be valid but is???
const props3: Props = {a: 1, b: 2, x: 3};             // Shouldn't be valid and indeed isn't
const props4: Props = {a: 1, b: 2, c: 3, d: 4};       // Should be valid and is
const props5: Props = {a: 1, b: 2, c: 3, d: 4, e: 5}; // Shouldn't be valid and indeed isn't

在尝试理解TypeScript的并集方法时,我也遇到了类似的、意外的props4行为:

type CommonProps = {a:number, b: number};
type OptionalProps1 = {c: number};
type OptionalProps2 = {d: number};
type Props = CommonProps & (OptionalProps1 | OptionalProps2)
const props1: Props = {a: 1, b: 2};                   // Shouldn't be valid and indeed isn't
const props2: Props = {a: 1, b: 2, c: 3};             // Should be valid and is
const props3: Props = {a: 1, b: 2, x: 3};             // Shouldn't be valid and indeed isn't
const props4: Props = {a: 1, b: 2, c: 3, d: 4};       // Shouldn't be valid but is?
const props5: Props = {a: 1, b: 2, c: 3, d: 4, e: 5}; // Shouldn't be valid and indeed isn't

在评估并集时,TypeScript允许对象在独立的每个字段的基础上进行匹配,并且只强制类型遵循单个";路径";一经阅读(如在受歧视的工会中)。因此,尚不清楚TypeScript并集是表示XOR运算还是简单的or运算。

在尝试复制真正的XOR操作时,我尝试(基于第一个示例):

type Props = (CommonProps & Record<keyof OptionalProps, never>) | (CommonProps & OptionalProps);

但这也没用。

是否可以在TypeScript中定义一个类型,该类型只接受两个互斥类型中的任何一个(而不是它们的混合类型),并且只允许读出这两个互斥型中的一个?

这是因为默认情况下不区分类型脚本联合。

type CommonProps = {a:number, b: number};
type OptionalProps = {c: number, d: number};
type Props = CommonProps | (CommonProps & OptionalProps)
const props2: Props = {a: 1, b: 2, c: 3}; // Shouldn't be valid but is???

上述类型对应于CommonProps,因为它已经具有ab属性。不考虑c属性。因此CCD_ 7可分配给CCD_ 8,这就是它有效的原因。

这里的类似案例:

type CommonProps = {a:number, b: number};
type OptionalProps1 = {c: number};
type OptionalProps2 = {d: number};
type Props = CommonProps & (OptionalProps1 | OptionalProps2)
const props4: Props = {a: 1, b: 2, c: 3, d: 4};       // Shouldn't be valid but is?

Props应包含ab以及cd。该值{a: 1, b: 2, c: 3, d: 4}可分配给Props,因为它具有所有这些值。您可以删除cd,但它仍然有效。

您正在寻找StrictUnion:

// credits goes to 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 CommonProps = { a: number, b: number };
type OptionalProps1 = { c: number };
type OptionalProps2 = { d: number };
type Props = CommonProps & StrictUnion<(OptionalProps1 | OptionalProps2)>
const props4: Props = { a: 1, b: 2, c: 3 }; //ok
const props4_1: Props = { a: 1, b: 2, d: 3 }; //ok
const props4_2: Props = { a: 1, b: 2, d: 3, c: 4 }; // expected error

游乐场

如果要使用有区别的并集,则应向并集中的每个类型添加相同的属性(例如kind),并分配唯一的类型。

最新更新