打字稿:重载函数的ReturnType

时间:2018-10-11 12:49:20

标签: typescript

ReturnType给出的类型似乎取决于重载签名的写入顺序

function applyChanges1(input: string): number
function applyChanges1(input: number): string
function applyChanges1(input: number | string): number | string {
  return typeof input === "number" ? input.toString() : input.length
}

function applyChanges2(input: number): string
function applyChanges2(input: string): number
function applyChanges2(input: number | string): number | string {
  return typeof input === "number" ? input.toString() : input.length
}

type Ret1 = ReturnType<typeof applyChanges1> // string
type Ret2 = ReturnType<typeof applyChanges2> // number

似乎采用了最后一个重载签名的返回类型,这似乎很随意。我期望Ret1Ret2都为string | number。有这种行为的原因吗?

3 个答案:

答案 0 :(得分:5)

正如Matt McCutchen指出的那样,这是ReturnType的局限性,在一般条件类型和多个重载签名中都是这样。

但是我们可以构造一个类型,该类型将返回所有重载的返回类型,最多返回任意数量的重载:

function applyChanges1(input: string): number
function applyChanges1(input: number): string
function applyChanges1(input: number | string): number | string {
return typeof input === "number" ? input.toString() : input.length
}

function applyChanges2(input: number): string
function applyChanges2(input: string): number
function applyChanges2(input: number | string): number | string {
return typeof input === "number" ? input.toString() : input.length
}


type OverloadedReturnType<T> = 
    T extends { (...args: any[]) : infer R; (...args: any[]) : infer R; (...args: any[]) : infer R ; (...args: any[]) : infer R } ? R  :
    T extends { (...args: any[]) : infer R; (...args: any[]) : infer R; (...args: any[]) : infer R } ? R  :
    T extends { (...args: any[]) : infer R; (...args: any[]) : infer R } ? R  :
    T extends (...args: any[]) => infer R ? R : any


type RetO1 = OverloadedReturnType<typeof applyChanges1> // string | number 
type RetO2 = OverloadedReturnType<typeof applyChanges2> // number | string

以上版本最多可用于4个重载签名(无论它们可能是什么),但可以轻松地(如果不是很漂亮的话)扩展到更多。

我们甚至可以通过相同的方式获得可能的参数类型的并集:

type OverloadedArguments<T> = 
    T extends { (...args: infer A1) : any; (...args: infer A2) : any; (...args: infer A3) : any ; (...args: infer A4) : any } ? A1|A2|A3|A4  :
    T extends { (...args: infer A1) : any; (...args: infer A2) : any; (...args: infer A3) : any } ? A1|A2|A3 :
    T extends { (...args: infer A1) : any; (...args: infer A2) : any } ? A1|A2  :
    T extends (...args: infer A) => any ? A : any


type RetO1 = OverloadedArguments<typeof applyChanges1> // [string] & [number]
type RetO2 = OverloadedArguments<typeof applyChanges2>  // [number] & [string]

答案 1 :(得分:1)

这是known limitation。 TypeScript团队的建议是包括“最一般”的过载签名作为您的最后一个过载签名,例如:

function applyChanges1(input: string): number
function applyChanges1(input: number): string
function applyChanges1(input: number | string): number | string
function applyChanges1(input: number | string): number | string {
  return typeof input === "number" ? input.toString() : input.length
}

Titian Cernicova-Dragomir在他的答案中有一个更好的替代解决方案。

答案 2 :(得分:1)

我遇到了类似的问题-我需要根据我的自变量来选择 exact const { MyClass4TS } = require('MyClass4TS'); const instance = new MyClass4TS();

例如:

ReturnType

因此,我根据上面@Titian的漂亮答案并使用function applyChanges1(input: string): number function applyChanges1(input: number): string function applyChanges1(input: boolean): object function applyChanges1(input: number | string | boolean): number | string | object { return typeof input === "number" ? input.toString() : typeof input === "boolean" ? { input } : input.length; } // Needed: type Ret11 = ReturnTypeWithArgs<typeof applyChanges1, [string]> // number type Ret12 = ReturnTypeWithArgs<typeof applyChanges1, [number]> // string type Ret13 = ReturnTypeWithArgs<typeof applyChanges1, [boolean]> // object type Ret14 = ReturnTypeWithArgs<typeof applyChanges1, [number | string]> // number | string type Ret15 = ReturnTypeWithArgs<typeof applyChanges1, [number | boolean]> // string | object type Ret16 = ReturnTypeWithArgs<typeof applyChanges1, [number | string | boolean]> // number | string | object 创建了以下ReturnTypeWithArgs实用程序。

Extract

它就像一种魅力!

Playground Link


PS。在我的真实情况下,我有7个超载,所以祝我好运! ; D