如何让函数返回类型依赖于局部变量的类型?
嘿,这个问题我太懂了!手动写一堆ReturnType<XXX> & ReturnType<YYY>确实又啰嗦又难维护,尤其是以后要加新的校验函数,简直是给自己挖坑。给你几个实用的解决办法,从简单到进阶都有:
最简单的:让TypeScript自动帮你推断
其实你根本不用手动指定返回类型!TypeScript本身就会根据函数里返回的局部变量类型,自动推断出函数的返回类型。比如你原来的代码,只要去掉显式的返回类型声明,TS就会自动把problems的合并类型作为validateAllTheThings的返回类型:
class WrapperClass { validateA(things: Thing[]) { return { badA: [] as string[], badB: [] as number[] }; } validateB(things: Thing[]) { return { anotherBadA: [] as string[], anotherBadB: [] as number[] }; } validateAllTheThings(things: Thing[]) { const problems = { ...this.validateA(things), ...this.validateB(things), }; return problems; } }
现在你鼠标悬停在validateAllTheThings上,就能看到它的返回类型自动变成了两个校验函数返回类型的交叉合并版,完全不用你手动写!
进阶版:支持动态扩展校验函数
如果以后要加validateC、validateD之类的函数,每次都要手动改返回类型太麻烦?那可以把所有校验函数放到一个数组里,用类型工具自动合并它们的返回类型:
首先定义一个递归的类型工具,用来合并数组中所有函数的返回类型:
// 递归合并数组中每个函数的返回类型 type MergeReturnTypes<T extends readonly ((...args: any[]) => any)[]> = T extends [infer First, ...infer Rest] ? ReturnType<First> & MergeReturnTypes<Rest> : {};
然后在类里把校验函数存成一个只读数组(用as const让TS保留每个函数的具体类型),再用这个类型工具指定返回类型:
class WrapperClass { // 把所有校验函数放这里,以后加新的直接添加就行 private validators = [this.validateA, this.validateB] as const; validateA(things: Thing[]) { return { badA: [] as string[], badB: [] as number[] }; } validateB(things: Thing[]) { return { anotherBadA: [] as string[], anotherBadB: [] as number[] }; } validateAllTheThings(things: Thing[]): MergeReturnTypes<typeof this.validators> { // 用reduce合并所有校验结果,类型也会自动匹配 const problems = this.validators.reduce((acc, validator) => ({ ...acc, ...validator(things), }), {} as MergeReturnTypes<typeof this.validators>); return problems; } }
现在不管你往validators数组里加多少个新的校验函数,validateAllTheThings的返回类型都会自动更新,完全不用手动修改类型声明,扩展性拉满!
如果你非要显式引用返回类型
如果需要在其他地方用到这个合并后的类型,也可以把它提取出来,但还是尽量用上面的自动方式:
// 提取合并后的类型 type ValidationResults = ReturnType<WrapperClass['validateA']> & ReturnType<WrapperClass['validateB']>; // 然后用在函数里 validateAllTheThings(things: Thing[]): ValidationResults { const problems = { ...this.validateA(things), ...this.validateB(things), }; return problems; }
不过这个方案还是要手动加每个校验函数的ReturnType,不如数组的方式灵活,适合校验函数数量固定的场景。
备注:内容来源于stack exchange,提问作者Carcigenicate

