TypeScript 如何正确为带可选参数的函数定义类型
问题描述
现有如下TypeScript函数定义:
function test<S, T>(obj: S, prop: keyof S, mapper?: (value: S[keyof S]) => T): S[keyof S] | T { return typeof mapper === 'function' ? mapper(obj[prop]) : obj[prop]; }
不传入mapper参数调用时,返回值类型无法被正确推导:
const value = test({ a: 'stringValue' }, 'a'); // value 类型被推导为 unknown
但传入恒等函数作为第三个参数时,返回值类型可以被正确推导:
const value = test({ a: 'stringValue' }, 'a', x => x); // value 类型被正确推导为 string
需要调整test函数的类型定义,实现未传入mapper参数时返回值类型也能被正确推导。
问题原因
原有类型定义存在两个核心问题:
- 没有为传入的属性键
prop声明独立泛型约束,使用S[keyof S]只能拿到对象所有属性值的联合类型,无法匹配传入单个属性对应的精确值类型,即使传入mapper也无法精准推导对应属性的类型 - 泛型参数
T仅在可选的mapper参数中被引用,当未传入mapper时,TypeScript无法推断T的实际类型,会默认将其填充为unknown,导致最终返回值类型被折叠为unknown
解决方案
使用TypeScript函数重载分别定义两种调用场景的类型,同时为属性键增加独立泛型参数,用来推导精确的属性值类型:
// 重载1:未传入mapper,直接返回对应属性的精确类型 function test<S, K extends keyof S>(obj: S, prop: K): S[K]; // 重载2:传入mapper,返回mapper处理后的结果类型 function test<S, K extends keyof S, T>(obj: S, prop: K, mapper: (value: S[K]) => T): T; // 函数实现,内部类型兼容所有重载场景即可 function test<S, K extends keyof S, T>(obj: S, prop: K, mapper?: (value: S[K]) => T): S[K] | T { return typeof mapper === 'function' ? mapper(obj[prop]) : obj[prop]; }
效果验证
调整类型后,所有调用场景的返回值都能被正确推导:
const value1 = test({ a: 'stringValue', b: 123 }, 'a'); // 推导为string类型 const value2 = test({ a: 'stringValue', b: 123 }, 'b'); // 推导为number类型 const value3 = test({ a: 'stringValue', b: 123 }, 'a', x => x.length); // 推导为number类型 const value4 = test({ a: 'stringValue', b: 123 }, 'b', x => x.toString()); // 推导为string类型
内容的提问来源于stack exchange,提问作者DMZ
相关产品推荐
相关产品推荐

