ReturnType<T> 和 Parameters<T> 是 TypeScript 的工具类型,它们允许自动推导函数的返回值类型(ReturnType)或其参数类型的数组(Parameters)。
这在确保应用程序不同部分之间类型一致性和实现泛型包装时特别方便。
使用示例:
function fn(a: number, b: string): boolean { return b.length > a; } type FnReturn = ReturnType<typeof fn>; // boolean type FnParams = Parameters<typeof fn>; // [number, string]
关于函数重载的注意事项: 在函数重载时,ReturnType 会定义最后一个签名变体,而 Parameters 会定义所有可能的重载:
function overloaded(x: number): number; function overloaded(x: string): string; function overloaded(x: any): any { return x; } type OverRet = ReturnType<typeof overloaded>; // any type OverParams = Parameters<typeof overloaded>; // [any]
也就是说,工具类型的类型化并不总是“看到”所有的重载,因此静态类型化变得不那么可预测。
能否通过 ReturnType 和 Parameters 获取自定义函数所有可能重载的类型,分别确定每个重载?
答案:
不可以。工具类型 ReturnType 和 Parameters 仅分析函数的“组合”签名——它是如何描述的以供实现。它们不允许获取每个单独重载的类型——仅允许获取最终实现的签名。
示例:
type P = Parameters<typeof overloaded>; // [any],而不是 [number],[string]
故事
开发人员使用 ReturnType<T> 对返回值进行类型化,编写了 aroundMethod 包装器。此时,包装函数应用于重载函数。结果:类型过于泛化(any),且编译器在出现错误时未能发出返回值不匹配的信号。在处理 boolean 时,最终发现了一个与 string 相关的 bug。
故事
开发人员在通过 Parameters<T> 推导多个 API 函数的参数时,未考虑方法的重载。导致了“错误”类型的问题,预期是 [string, number],结果却是 [any]。单元测试未能捕捉到此错误,因为通过实现进行的装配,而 API 的真实用户在生产环境中遇到了 bug。
故事
在将大量代码从 JavaScript 迁移到 TypeScript 时,开发人员通过 ReturnType 进行所有类型化。后来意识到实现与重载声明差异很大。这导致在处理错误参数时出现了意外的运行时异常(例如,TypeError: x is undefined)。