TypeScript约束对象符合指定类型且保留精确类型推断的方法
TypeScript 实现严格结构约束+精确类型推断方案
实现思路
要同时满足「符合目标结构、禁止多余属性、精确推断实际属性」三个要求,不能直接用类型注解、普通泛型约束或者satisfies运算符——前两种会把类型放宽到目标类型,导致未声明的可选属性可访问;普通泛型约束和satisfies仅做子类型兼容校验,不会拦截额外属性。
正确方案是通过递归映射类型实现精确结构匹配校验,配合泛型恒等函数完成类型推断。
完整实现代码
// 目标类型(已定义的TypeA) type TypeA = { a: number; b: { c: number; d?: string; e?: boolean; } } // 递归精确校验工具类型:保证T严格匹配Shape结构,不存在Shape外的多余属性 type Exact<T, Shape> = T extends Shape ? { [K in keyof T]: K extends keyof Shape ? Exact<T[K], Shape[K]> : never; } : never; // 受约束的构建函数 function buildTypeA<T extends TypeA>(obj: T & Exact<T, TypeA>) { return obj; }
实际效果
合法场景(正常通过,类型精确收窄)
传入示例中的对象:
const myObj = buildTypeA({ a: 1, b: { c: 2, d: 'hello', } });
此时TS推断出的myObj类型完全和传入的结构一致:
- 可正常访问已声明的
a、b.c、b.d属性,类型完全匹配 - 访问未声明的
b.e时,TS会直接抛出「Property 'e' does not exist on type」的错误,完全符合预期。
非法场景(直接触发类型报错)
以下不符合要求的写法都会被TS拦截:
- 顶层新增TypeA未定义的属性:
const badCase1 = buildTypeA({ a: 1, b: { c: 2 }, extraProp: 123 // ❌ 类型错误:number不能赋值给never })
- 嵌套对象内新增未定义属性:
const badCase2 = buildTypeA({ a: 1, b: { c: 2, extraNested: 'test' // ❌ 类型错误:多余属性不允许 } })
- 缺失必填属性(比如漏写
a、b.c),会正常触发必填属性缺失的报错。
注意点
- 该工具类型是递归实现的,支持任意深度的嵌套对象校验,不会遗漏深层结构的多余属性
- 如果显式传入了可选属性(比如
e: true或e: undefined),该属性会被加入最终推断类型,访问不会报错,符合代码实际逻辑 - 对于其他需要相同约束的类型,直接复用
Exact工具类型写对应构建函数即可,无需重复实现校验逻辑
内容的提问来源于stack exchange,提问作者Togira
相关产品推荐
相关产品推荐

