我的问题可以用这个小片段来总结(这是Playground中较大的交互式示例):
type X = {x: number};
type Y = {y: number};
type XXY = { x: X } & Y;
let xxy: XXY = {
x: {
x: 1,
notValid: 1 // <--- this is not an error :(
},
y: 1
};
鉴于X
和Y
是通过另一种方式派生的(所以我不能只手工编写XXY
类型),如何使它成为未知键在嵌套对象中被视为无效?
答案 0 :(得分:1)
这是known bug,其中excess property checking不适用于人们期望的涉及联合和交集的嵌套类型。多余的属性检查是类型系统的一个附加组件,它仅适用于对象文字,因此当它不适用时,情况会退回到structural subtyping规则中,其中类型{a: A, b: B}
是子类型{a: A}
的值,因此前一种类型的值应可分配给后一种类型的变量。如果您认为此案例比已经列出的案例更具吸引力,那么您可能想要转到the issue in Github并给出一个案例或解释您的用例。希望有一天会解决此问题。
在此之前,有解决方法。等同于多余属性检查的类型级别称为exact types,它在TypeScript中不作为具体类型存在。有多种方法可以使用通用辅助函数进行模拟并进行类型推断...在您的情况下,它看起来像这样:
type Exactly<T, U extends T> = T extends object ?
{ [K in keyof U]: K extends keyof T ? Exactly<T[K], U[K]> : never }
: T
const asXXY = <T extends XXY>(x: T & Exactly<XXY, T>): T => x;
let xxy = asXXY({
x: {
x: 1,
notValid: 1 // error!
},
y: 1
});
那是你想要的错误,对吧?
工作原理:辅助函数asXXY<T extends XXY>(t: T & Exactly<XXY, T>)
推断通用类型T
为传入参数t
的类型。然后,它尝试评估交点T & Exactly<XXY, T>
。如果t
可分配给T & Exactly<XXY, T>
,则检查成功,并且该函数可调用。否则,t
某处会出现错误,表明它们之间的区别。
然后Exactly<T, U extends T>
基本上递归地遍历U
,只要它与T
匹配就保持不变...否则,将属性设置为never
。
在上述情况下,我们将扩展此差异:T
的推断类型为
{ x: { x: number; notValid: number; }; y: number; }
可以分配给T & Exactly<XXY, T>
吗?好吧,Exactly<XXY, T>
是什么?原来是
{ x: { x: number; notValid: never; }; y: number; }
当您深入到类型T
并注意notValid
中找不到XXY
时会发生这种情况。
交集T & Exactly<XXY, T>
本质上只是Exactly<XXY, T>
,所以现在编译器正在将传入的参数与类型进行比较
{ x: { x: number; notValid: never; }; y: number; }
不是。 notValid
类型是number
,而不是never
...,因此编译器会在您想要的位置抱怨。
好的,希望能有所帮助。祝你好运!