TypeScript泛型约束异常:可识别错误类型,但允许多余属性
TypeScript泛型约束未拦截对象多余属性的问题
我最近做了个可视化测试(就是在VS Code里检查代码有没有红色波浪线),想验证泛型类型能不能正确约束substitute()方法的入参。测试代码是这样的:
interface IJob { jobId: string; extraneous?: number; } /** * Bear in mind these tests are just to visually identify in * the editor that the generics are passing their types around * correctly. */ describe("Typings →", () => { it.skip('setting <T> imposes structure on the substitutions that are allowed in', () => { const wrongType = { jobId: "1234", extraneous: "foobar" } const nonExistantType = { jobId: "1234", noCanDo: true } const ruleAbidingJob = { jobId: "1234" } const t = TypedTemplate.create<IJob>(); t.substitute(wrongType); // this should be failing in TS t.substitute(nonExistantType); // this should be failing in TS t.substitute(ruleAbidingJob); // this should be fine }); });
测试结果有点奇怪:
ruleAbidingJob完全符合IJob的结构,能正常传入,没问题;wrongType里的extraneous属性传了字符串(而接口里定义的是可选number),TypeScript正确识别并标红了,这也符合预期;- 但
nonExistantType带了一个IJob里根本没定义的noCanDo属性,VS Code里居然没报错!而且我确认过substitute方法的预期参数类型确实是IJob。
原因分析
这其实是TypeScript的一个特性——对象字面量超额属性检查。简单说就是:当你直接把对象字面量传给函数参数时,TS会严格检查有没有多余属性;但如果先把对象赋值给一个变量,再把变量传进去,TS会认为这个变量可能是IJob的兼容扩展类型,不会触发超额属性检查。
解决办法
如果想要严格拦截多余属性,有几个办法可以试试:
直接传入对象字面量
不要先赋值给变量,直接把对象写在substitute的参数里:t.substitute({ jobId: "1234", noCanDo: true }); // 这里会立刻报错用类型断言或工具函数约束变量类型
强制把变量约束为严格的IJob类型,这样在变量定义时就会报错:// 类型断言方式 const nonExistantType = { jobId: "1234", noCanDo: true } as IJob; // 此处报错 // 工具函数方式(更直观) function strictType<T>(obj: T): T { return obj; } const nonExistantType = strictType<IJob>({ jobId: "1234", noCanDo: true }); // 此处报错给
substitute方法定义严格的参数类型
可以自己实现一个Exact工具类型,让substitute的参数必须严格匹配T,不允许任何多余属性:// 定义Exact工具类型 type Exact<T, U> = T extends U ? U extends T ? T : never : never; // 修改TypedTemplate的create方法返回值 class TypedTemplate { static create<T>() { return { substitute(obj: Exact<T, typeof obj>) { // 方法逻辑... } } } }这样不管是传变量还是直接传字面量,只要有多余属性都会被拦截。
内容的提问来源于stack exchange,提问作者ken
相关产品推荐
相关产品推荐

