讨论中的问题微软/TypeScript#14094在这里相关。
TypeScript 中的类型有open从某种意义上说,一个对象必须具有at least类型所描述的属性以使其匹配。所以对象{ value: 7, data: 'test', note: 'hello' }
匹配类型{ value: number, data: string }
,即使它有多余的note
财产:
const obj = { value: 7, data: 'test', note: 'hello' };
const val: sthA = obj; // okay
So your c
变量确实是有效的sth
。它只会失败sth
如果是的话missing工会某些组成部分所需的所有属性:
// error: missing both "data" and "note"
const oops: sth = { value: 7 };
但是:当您将新的对象文字分配给 TypeScript 中的类型变量时,它会执行超额财产检查尽量避免错误。这具有在该赋值期间“关闭”TypeScript 的开放类型的效果。这正如您对接口类型所期望的那样。但对于工会来说,TypeScript 目前(如这条评论)仅抱怨未出现的属性any的成分。所以下面的仍然是一个错误:
// error, "random" is not expected:
const alsoOops: sth = { value: 7, data: 'test', note: 'hello', random: 123 };
但 TypeScript 目前并没有按照您想要的严格方式对联合类型进行过多的属性检查,它会根据每个组成类型检查对象文字,并抱怨所有类型中是否存在额外的属性。它确实做到了这一点受歧视的工会,如中提到的微软/TypeScript#12745,但这并不能解决你的问题,因为这两个定义都没有sth
是受歧视的(意思是:拥有一个其字面类型恰好挑选出联合的一个组成部分的属性)。
因此,除非这一点发生改变,否则最好的解决方法可能是在使用对象文字时避免联合,方法是显式分配给预期的组成部分,然后如果需要的话稍后扩大到联合:
type sthA = { value: number, data: string };
type sthB = { value: number, note: string };
type sth = sthA | sthB;
const a: sthA = { value: 7, data: 'test' };
const widenedA: sth = a;
const b: sthB = { value: 7, note: 'hello' };
const widenedB: sth = b;
const c: sthA = { value: 7, data: 'test', note: 'hello' }; // error as expected
const widenedC: sth = c;
const cPrime: sthB = { value: 7, data: 'test', note: 'hello' }; // error as expected
const widenedCPrime: sth = cPrime;
If you really想要表达一个独家的对象类型的联合,您可以使用mapped and 有条件的类型来做到这一点,通过将原始联合转换为新联合,其中每个成员通过将它们添加为 type 的可选属性来显式禁止来自联合其他成员的额外键never
(显示为undefined
因为可选属性总是可以undefined
):
type AllKeys<T> = T extends unknown ? keyof T : never;
type Id<T> = T extends infer U ? { [K in keyof U]: U[K] } : never;
type _ExclusifyUnion<T, K extends PropertyKey> =
T extends unknown ? Id<T & Partial<Record<Exclude<K, keyof T>, never>>> : never;
type ExclusifyUnion<T> = _ExclusifyUnion<T, AllKeys<T>>;
有了这个,你就可以“排除”sth
into:
type xsth = ExclusifyUnion<sth>;
/* type xsth = {
value: number;
data: string;
note?: undefined;
} | {
value: number;
note: string;
data?: undefined;
} */
现在会出现预期的错误:
const z: xsth = { value: 7, data: 'test', note: 'hello' }; // error!
/* Type '{ value: number; data: string; note: string; }' is not assignable to
type '{ value: number; data: string; note?: undefined; } |
{ value: number; note: string; data?: undefined; }' */
Playground 代码链接