TypeScript 泛型函数中恢复「对象字面量仅允许已知属性」校验的方法
原理说明
TypeScript 的「对象字面量多余属性校验」属于针对直接赋值场景的语法糖校验,并非结构类型系统的强制规则。当对象字面量先传入泛型函数时,TypeScript 会优先将对象字面量的全量属性(包含多余属性)推导为泛型参数的实际类型,经过转换后的返回值属于目标类型的子类型,结构类型系统默认允许子类型赋值给父类型,因此不会触发多余属性校验。
改造方案
核心思路是利用 TypeScript 的上下文类型推导能力,给泛型函数新增一个默认推导的泛型参数,承接返回值接收方的目标类型,将入参中不在目标类型上的属性标记为never类型,只要传入多余属性就会触发类型报错,变相恢复多余属性校验能力。
1. 恒等函数identity改造示例
declare function setUser(arg: {name: string}): void; // 改造后的identity declare function identity<T, Expected = T>( v: T extends Expected ? T & Record<Exclude<keyof T, keyof Expected>, never> : never ): T; setUser({name: 'a', age: 12}); // 报错,符合预期 setUser(identity({name: 'a', age: 12})); // 现在会报错,符合预期 const u = {name: 'a', age: 12}; setUser(u); // 无报错,属于已知的规则例外,符合预期
2. 自定义NullableToOptional转换函数改造示例
declare function setUser(arg: {name: string}): void; type NonNullable<T> = T extends null ? never : T; export type NullableToOptional<T> = { [K in keyof T]: null extends T[K] ? NonNullable<T[K]> | undefined : T[K]; }; // 改造后的toOptional函数 export function toOptional<T, Expected = T>( x: T extends Expected ? T & Record<Exclude<keyof T, keyof Expected>, never> : never ): NullableToOptional<T> { return x as NullableToOptional<T>; } setUser({name: 'a', age: 12}); // 报错,符合预期 setUser(toOptional({name: 'a', age: 12})); // 现在会报错,符合预期
注意事项
该改造方案的校验行为和原生多余属性校验完全对齐:如果泛型函数的返回值没有直接传入明确类型的参数/变量,而是先赋值给未显式标注类型的临时变量,上下文类型推导失效,不会触发多余属性校验,该表现和原生的const u = {name: 'a', age:12}; setUser(u)不报错的逻辑一致。
内容的提问来源于stack exchange,提问作者Kannan Goundan
相关产品推荐
相关产品推荐

