如何让TypeScript在函数体内识别参数及解构对象属性的关联?
如何让TypeScript在函数体内理解解构对象参数的两个属性之间的关联?
我尝试了两种写法来限制函数参数的合法性,但函数内部始终无法正确推断两个属性的关联类型:
type Type<T extends string | number> = { one: T; two: T extends string ? number : string; }; function foo<T extends string | number>({ one, two }: Type<T>) { if (typeof one === "string") { two; // 这里无法推断two是number类型 } else { two; // 这里无法推断two是string类型 } } foo({one: 'one', two: 'two'}) // 调用时报错,符合预期 foo({one: 'one', two: 2}) // 合法调用 function bar({ one, two }: { one: string; two: number }): void; function bar({ one, two }: { one: number; two: string }): void; function bar<T extends string | number>({ one, two, }: { one: string | number; two: string | number; }): void { if (typeof one === "string") { two; // 同样无法推断two的类型 } else { two; // 同样无法推断two的类型 } } bar({one: 'one', two: 'two'}) // 调用时报错,符合预期 bar({one: 'one', two: 2}) // 合法调用
解决方法
问题出在解构后变量丢失了原对象的类型关联,TypeScript无法单独跟踪one和two之间的约束关系。以下是几种可行的修复方式:
1. 保留完整的参数对象,避免解构
通过访问原对象的属性,TypeScript可以基于one的类型窄化整个对象的类型,从而正确推断two的类型:
type Type<T extends string | number> = { one: T; two: T extends string ? number : string; }; function foo<T extends string | number>(param: Type<T>) { if (typeof param.one === "string") { param.two; // 此时TypeScript能准确推断出two是number类型 } else { param.two; // 此时TypeScript能准确推断出two是string类型 } } foo({one: 'one', two: 2}) // 合法 foo({one: 'one', two: 'two'}) // 类型错误,符合预期
2. 使用联合类型定义参数,配合对象窄化
直接把参数定义为合法类型的联合,函数内部通过判断one的类型来窄化整个参数的类型:
type ValidParams = | { one: string; two: number } | { one: number; two: string }; function baz(param: ValidParams) { if (typeof param.one === "string") { param.two; // 自动推断为number类型 } else { param.two; // 自动推断为string类型 } } baz({one: 'one', two: 2}) // 合法 baz({one: 1, two: 'two'}) // 合法 baz({one: 'one', two: 'two'}) // 类型错误,符合预期
3. 自定义类型守卫(适合必须解构的场景)
如果必须解构变量,可以用自定义类型守卫手动关联one和two的类型:
// 自定义类型守卫,声明当one是string时,two必然是number function isStringOne( one: string | number, two: string | number ): one is string { return typeof one === "string"; } function bar({ one, two }: { one: string; two: number }): void; function bar({ one, two }: { one: number; two: string }): void; function bar({ one, two }: { one: string | number; two: string | number }): void { if (isStringOne(one, two)) { two satisfies number; // 此时two被推断为number类型 } else { two satisfies string; // 此时two被推断为string类型 } } bar({one: 'one', two: 2}) // 合法 bar({one: 'one', two: 'two'}) // 类型错误,符合预期
内容的提问来源于stack exchange,提问作者Magnus
相关产品推荐
相关产品推荐

