TypeScript泛型推断时如何限制对象仅允许指定键?
在TypeScript中约束泛型对象仅允许已知键
要实现你期望的「传入含额外键的对象时函数报错」的效果,需要手动添加精确类型约束——因为TypeScript的结构子类型系统默认允许子类型包含额外属性,泛型T extends Data会直接推断出包含额外键的类型(比如{a:number; b:number; c:number}),而这个类型确实符合extends Data的条件,所以不会报错。
解决方案:自定义Exact工具类型
可以通过自定义工具类型Exact强制泛型T的键只能是目标类型的已知键:
type Data = { a: number; b: number; }; // 工具类型:约束T的键完全匹配Base的键,不允许额外属性 type Exact<Base, T> = T extends Base ? Exclude<keyof T, keyof Base> extends never ? T : never : never; // 使用Exact约束泛型T function foo<T extends Exact<Data, T>>(o: T) {} foo({a:8,b:4,c:8}) // 报错:类型"{ a: number; b: number; c: number; }"不满足约束"Exact<Data, { a: number; b: number; c: number; }>" foo({a:8,b:4}) // 正常通过
原理说明
Exact<Base, T>先检查T是否是Base的子类型;- 再通过
Exclude<keyof T, keyof Base>判断T是否包含Base没有的键:如果有,结果不为never,则Exact返回never,导致泛型约束不通过,触发报错; - 只有当
T的键完全等于Base的键时,Exact才会返回T,允许函数调用。
简洁写法:直接在泛型约束中检查
如果不想单独定义工具类型,也可以直接在函数的泛型约束中合并检查逻辑:
function foo<T extends Data>(o: T & (Exclude<keyof T, keyof Data> extends never ? {} : never)) {} foo({a:8,b:4,c:8}) // 同样会触发报错
注意事项
- 当传入一个预先定义好的变量(而非直接传入字面量)时,只要变量类型包含额外键,同样会触发报错;
- 如果将含额外键的变量断言为
Data类型,函数调用不会报错(因为断言后的类型已经丢失了额外键信息)。
内容的提问来源于stack exchange,提问作者jon
相关产品推荐
相关产品推荐

