我的问题可以用这个小片段来总结(这里是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
类型),我如何才能使其使嵌套对象中的未知键被视为无效键? 最佳答案
这是一个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
的类型。然后尝试对交集(cc)进行评价。如果T & Exactly<XXY, T>
可分配给t
,则检查成功,函数可调用。否则,T & Exactly<XXY, T>
上的某个地方会出现错误,显示它们的不同之处。并且
t
基本上递归地通过Exactly<T, U extends T>
向下移动,保持它与匹配U
的长度相同。否则它会将属性设置为T
。让我们在上面的例子中对这个差异进行扩展:
{ x: { x: number; notValid: number; }; y: number; }
是否可分配给
never
?那么,什么是T
?原来是{ x: { x: number; notValid: never; }; y: number; }
这就是当你深入到
T & Exactly<XXY, T>
类型并注意到Exactly<XXY, T>
中找不到T
时会发生的情况。交叉点
notValid
本质上只是XXY
,所以现在编译器将传入的参数与类型进行比较。{ x: { x: number; notValid: never; }; y: number; }
不是的,
T & Exactly<XXY, T>
类型是aExactly<XXY, T>
,而不是anotValid
。所以编译器会在你想要的地方抱怨。好吧,希望能帮上忙。祝你好运!