如何定义不允许包含指定字段(甚至不能设为undefined)的对象类型?
问题描述
编写了合并对象的TypeScript函数,希望在编译阶段阻止addition参数包含baseField字段(即使设为undefined),避免运行时覆盖原对象的字段。现有方案使用baseField?: never无法检测到设为undefined的情况,导致编译不报错但运行时baseField被意外覆盖。
原实现代码:
interface BaseType { baseField: string; } function combine<T extends Object>(base: BaseType, addition: T) { if('baseField' in addition) throw new Error('addition should not have baseField'); return {...base, ...addition}; } const myBase : BaseType = { baseField: "xyz" } const myAddition = { otherField: 123 } const ret = combine(myBase, myAddition); // --> ret = { baseField: "xyz", otherField: 123} console.log(ret);
尝试的无效方案:
interface BaseType { baseField: string; } type RestrictionType = Object & { baseField?: never } function combine<T extends RestrictionType>(base: BaseType, addition: T) { return {...base, ...addition}; } const myBase : BaseType = { baseField: "xyz" } const myAddition = { otherField: 123, baseField: undefined // 显式设为undefined } const ret = combine(myBase, myAddition); // 无编译警告,但运行时baseField被覆盖 // --> ret = { baseField: undefined, otherField: 123 } console.log(ret);
解决方案
要实现绝对禁止对象包含指定字段(无论值是什么),可以通过约束泛型类型的键与禁止键完全不相交来实现:
- 定义类型工具,检查对象是否包含禁止键,若包含则返回
never触发编译错误:
// 禁止T包含U中的任何键 type ProhibitKeys<T, U extends object> = keyof T extends Exclude<keyof T, keyof U> ? T : never;
- 修改
combine函数的泛型约束,确保addition参数符合要求:
interface BaseType { baseField: string; } type ProhibitKeys<T, U extends object> = keyof T extends Exclude<keyof T, keyof U> ? T : never; function combine<T extends ProhibitKeys<T, BaseType>>(base: BaseType, addition: T) { return {...base, ...addition}; } const myBase: BaseType = { baseField: "xyz" } // 合法:无baseField字段 const validAddition = { otherField: 123 } const ret1 = combine(myBase, validAddition); // 编译通过 // 非法:显式设置baseField为undefined const invalidAddition1 = { otherField: 123, baseField: undefined } const ret2 = combine(myBase, invalidAddition1); // 编译错误:类型不满足约束 // 非法:显式设置baseField为字符串 const invalidAddition2 = { otherField: 123, baseField: "abc" } const ret3 = combine(myBase, invalidAddition2); // 编译错误
原理说明
keyof T extends Exclude<keyof T, keyof U>:检查T的所有键是否都不在U的键集合中。如果T包含U的任意键,Exclude<keyof T, keyof U>会排除这些键,导致keyof T与排除后的类型不匹配,最终返回never触发编译错误。- 这种约束会严格检查对象的键,无论对应值是
undefined还是其他类型,只要存在禁止键就会报错,彻底避免运行时覆盖问题。
内容的提问来源于stack exchange,提问作者Hauke P.
相关产品推荐
相关产品推荐

