TypeScript如何修改函数类型返回新值?实现ReturnValueMapper泛型
首先得明确:你最初写的ReturnValueMapper确实没法直接实现,因为TypeScript的类型系统里,没办法直接从泛型约束Func里提取出它的参数类型Args并复用——你写的Args[]在这里是未定义的类型参数,TypeScript没法自动推断它。不过别担心,我们有更优雅的方式来实现这个需求。
正确的ReturnValueMapper实现方式
我们可以利用TypeScript的条件类型和infer关键字来提取原函数的参数类型,然后重新构造一个参数相同但返回值替换的函数类型:
export type ReturnValueMapper<Func extends (...args: any[]) => any, ReturnValue> = Func extends (...args: infer Args) => any ? (...args: Args) => ReturnValue : never;
这里的infer Args会自动从Func中提取出它的参数列表类型,然后我们用这个Args来构造新的函数类型,返回值替换成指定的ReturnValue。这样就完美实现了“保留原函数参数,替换返回值”的需求。
结合recompose的withStateHandlers优化场景
回到你的使用场景:你希望定义状态更新器时,不用显式指定Partial<State>,而是让类型系统自动适配。假设你的State类型是这样的:
interface State { name: string; age: number; }
你可以用刚才的ReturnValueMapper来包装状态更新器的类型,或者直接在withStateHandlers的类型定义里整合这个逻辑。比如,我们可以自定义一个更友好的状态更新器类型:
// 定义一个通用的状态更新器类型,接收原函数(比如获取新状态的函数),替换返回值为void type StateUpdater<State, UpdateFn extends (...args: any[]) => Partial<State>> = ReturnValueMapper<UpdateFn, void>; // 然后定义你的StateUpdaters interface StateUpdaters { // 这里的update函数参数和原更新函数一致,但返回值是void update: StateUpdater<State, (field: string) => Partial<State>>; }
或者更简洁地直接在StateUpdaters里使用条件类型:
interface StateUpdaters { update: ((field: string) => Partial<State>) extends (...args: infer Args) => any ? (...args: Args) => void : never; }
这样你就不用在update方法里显式写Partial<State>了,类型系统会自动帮你处理返回值的替换,同时保留原参数类型。
为什么最初的写法不可行?
你最初的写法里Args[]是一个未声明的类型参数,TypeScript的泛型约束里不能直接引用未定义的类型参数。泛型参数必须在<>里显式声明,而你试图从Func中“提取”参数类型,但没有用infer来告诉TypeScript去推断这个类型,所以编译器无法识别Args是什么,自然会报错。
内容的提问来源于stack exchange,提问作者Leonid Fenko

