如何消除TypeScript中modifyFn的Rest参数类型错误并保留功能?
解决TypeScript Rest参数类型错误问题
问题分析
现有代码可实现将接收Date类型参数的函数转为接收string类型参数的函数,但modifyFn中出现「Rest参数必须为数组类型」的错误。这是因为TypeScript在泛型上下文里,无法自动将MapTupleValues<Parameters<Fn>, string>推断为符合rest参数要求的数组/元组类型,尽管该类型本质是元组。
解决方案
方案一:用展开语法明确元组类型
修改modifyFn的返回函数参数类型,通过[...T]展开语法明确告知TypeScript这是可作为rest参数的元组类型:
const stringToDate = (x: string) => new Date(x) type MapTupleValues<Tuple extends any[], NewValue> = { [index in keyof Tuple]: NewValue } function modifyFn<Fn extends (...args: Date[]) => any>(fn: Fn) { // 用[...T]语法明确参数为可展开的元组类型 return (...args: [...MapTupleValues<Parameters<Fn>, string>]): ReturnType<Fn> => { return fn(...args.map(x => stringToDate(x))) } } const daysDifference = (start: Date, end: Date) => (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24) const modifiedDaysDifference = modifyFn(daysDifference) // modifiedDaysDifference: (start: string, end: string) => number console.log(daysDifference(new Date('2022-07-29'), new Date('2022-08-21'))) // 23 console.log(modifiedDaysDifference('2022-07-29', '2022-08-21')) // 23
方案二:拆分泛型参数简化类型推断
直接将泛型参数拆分为参数元组Args和返回值类型R,让类型映射更直观,同时通过类型断言解决map返回值的类型匹配问题:
const stringToDate = (x: string) => new Date(x) function modifyFn<Args extends readonly Date[], R>(fn: (...args: Args) => R) { return (...args: { [K in keyof Args]: string }): R => { // 断言map结果符合原函数的参数元组类型 return fn(...args.map(x => stringToDate(x)) as Args) } } const daysDifference = (start: Date, end: Date) => (end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24) const modifiedDaysDifference = modifyFn(daysDifference) // modifiedDaysDifference: (start: string, end: string) => number console.log(daysDifference(new Date('2022-07-29'), new Date('2022-08-21'))) // 23 console.log(modifiedDaysDifference('2022-07-29', '2022-08-21')) // 23
效果说明
两种方案都能消除类型错误,同时完全保留原有功能:modifiedDaysDifference仍会被正确推断为接收两个string参数并返回number的函数,运行结果与原函数一致。
内容的提问来源于stack exchange,提问作者Jacob Weisenburger
相关产品推荐
相关产品推荐

