如何解决TypeScript柯里化函数参数被推断为unknown的类型问题
问题原因与解决方案
根因分析
TypeScript 的泛型参数推断是按函数调用顺序即时执行的:你调用第一层函数 select(i => i.name) 时,还没有传入第二个数组参数,此时没有任何上下文可以推导泛型 T 的类型,TS 会直接将 T 推断为 unknown。等到第二层传入 items 时,泛型 T 已经固定为 unknown,自然会报「unknown 上不存在 name 属性」的错误。
这种先传选择器、后传数组的柯里化设计,本身无法实现完全自动的类型推断,因为选择器本身的入参类型需要依赖后续传入的数组类型推导,调用顺序和推断顺序是相反的。
可选解决方案
方案1:保留原柯里化顺序,调用时手动指定泛型
如果业务必须保持「先传选择器、后传数组」的调用顺序,只需要在调用第一层函数时手动标注泛型类型即可:
const select = <T>(selector: (i: T) => T) => (numbers: T[]) => numbers.map(selector); interface Item { name: string; } const items: Item[] = [{ name: 'name1' }, { name: 'name2' }]; // 手动指定泛型T为Item,类型正常不报错 select<Item>(i => i.name)(items);
注意:原示例的函数定义中选择器返回值类型为
T,如果你的实际场景需要返回选中的属性值(比如上述代码中返回string类型的name),可以扩展泛型支持自定义返回值:// 扩展支持返回任意类型的选择结果 const select = <T, R>(selector: (i: T) => R) => (numbers: T[]) => numbers.map(selector); // 调用时只需指定入参类型,返回值会自动推导 const names = select<Item, string>(i => i.name)(items); // 类型为string[]
方案2:调整柯里化顺序,实现全自动类型推断
把数组作为第一层函数的入参,调用时先传数组,TS 可以直接从数组类型推导出 T 的具体值,后续传入选择器时就能自动识别入参类型,不需要手动标注任何泛型:
// 调整柯里化顺序,先收数组,再收选择器 const select = <T>(numbers: T[]) => (selector: (i: T) => T) => numbers.map(selector); interface Item { name: string; } const items: Item[] = [{ name: 'name1' }, { name: 'name2' }]; // 自动推断T为Item,i的类型完全正确 select(items)(i => i.name);
这种方案的开发体验最优,也是社区主流的柯里化工具函数(比如Ramda、Lodash/fp)的通用设计逻辑:把固定/已知类型的参数放在柯里化的前几层,动态/需要推导类型的参数放在后几层。
方案3:放弃柯里化,合并为单函数调用
如果不需要复用选择器,直接把两个参数合并到同一个函数中,TS 会同时匹配两个参数的上下文推导泛型,也能实现自动类型推断:
const select = <T>(numbers: T[], selector: (i: T) => T) => numbers.map(selector); interface Item { name: string; } const items: Item[] = [{ name: 'name1' }, { name: 'name2' }]; // 自动推断i为Item类型 select(items, i => i.name);
内容的提问来源于stack exchange,提问作者surajs02
相关产品推荐
相关产品推荐

