Typescript:重载函数的ReturnType

Typescript:重载函数的ReturnType,typescript,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.

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
函数applyChanges1(输入:字符串):数字
函数applyChanges1(输入:number):字符串
函数applyChanges1(输入:数字|字符串):数字|字符串{
返回输入的类型===“number”?input.toString():input.length
}
函数applyChanges2(输入:数字):字符串
函数applyChanges2(输入:字符串):数字
函数applyChanges2(输入:数字|字符串):数字|字符串{
返回输入的类型===“number”?input.toString():input.length
}
type Ret1=ReturnType//string
类型Ret2=返回类型//编号
它似乎采用了最后一个重载签名的返回类型,这似乎相当随意。我希望
Ret1
Ret2
都是
string | number
。这种行为有什么原因吗?

这是一个错误。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在他的回答中有一个更好的替代解决方案。

正如Matt McCutchen指出的,这是返回类型的一个限制,通常是条件类型和多个重载签名

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

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
函数applyChanges1(输入:字符串):数字
函数applyChanges1(输入:number):字符串
函数applyChanges1(输入:数字|字符串):数字|字符串{
返回输入的类型===“number”?input.toString():input.length
}
函数applyChanges2(输入:数字):字符串
函数applyChanges2(输入:字符串):数字
函数applyChanges2(输入:数字|字符串):数字|字符串{
返回输入的类型===“number”?input.toString():input.length
}
类型重载ReturnType=
T扩展{(…args:any[]):推断R;(…args:any[]):推断R;(…args:any[]):推断R;(…args:any[]):推断R}?R:
T扩展{(…args:any[]):推断R;(…args:any[]):推断R;(…args:any[]):推断R}?R:
T扩展{(…args:any[]):推断R;(…args:any[]):推断R}?R:
T扩展(…args:any[])=>推断R?R:有吗
类型RetO1=重载RETURNTYPE//string | number
类型RetO2=重载RETURNTYPE//number |字符串
上面的版本最多可用于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]
类型重载参数=
T扩展{(…args:infera1):any;(…args:infera2):any;(…args:infera3):any;(…args:infera4):any}?A1 | A2 | A3 | A4:
T扩展{(…args:infera1):any;(…args:infera2):any;(…args:infera3):any}?A1 | A2 | A3:
T扩展{(…args:infera1):any;(…args:infera2):any}?A1 | A2:
T扩展(…参数:推断A)=>任何?A:有吗
类型RetO1=重载参数//[string]&[number]
键入RetO2=overloadearguments//[number]&[string]
我有一个类似的问题——我需要根据参数选择精确重载的
返回类型

e、 g:

它就像一个符咒



在我的真实情况下,我有7过载,所以祝我好运;D

是的,但这可以在没有额外过载的情况下完成,我建议了一种方法,它是在您评论的一个GitHub tickes中发现的:。您是否知道不建议这样做的原因?除了丑陋:)我想我只是回答了关于
ReturnType
的问题,没有考虑其他解决方案,而且我已经忘记了你的解决方案。谢谢你的发帖。非常有趣。我看到了你对其他打字问题的很多回答,这些问题一直都很有启发性。我很好奇你是如何获得这些知识的哈哈。@LionelTay不确定。。事情发生得很及时。不久前,我曾修补过编译器,这很有帮助,我还阅读了每一个realese版本的PR,它们非常有启发性。另外,回答问题hears有助于我积累知识:)嗯,所以我认为如果
F
是一个函数类型,即使它的重载签名少于四个,那么
OverloadedRetrunType
总是通过第一个“四重载”测试。几乎巧合的是,
R
的推断类型是正确的值(它成为成功推断类型的并集)。如果您试图将其分为不同的返回类型
inferr。。。推断
,您将得到大量的
{}
返回。这是令人沮丧的…@jcalz我也看到了同样的事情。如果您执行类似
T扩展{(…args:any[]):推断R1;(…args:any[]):推断R2;(…args:any[]):推断R3;(…args:any[]):推断R4;}?R1 | R2 | R3 | R4…代码的其余部分
相反,您可以清楚地看到它发生在联合体中。假设你有3个重载,那么你会得到
InferedType1 | InferedType2 | InferedTyp3 |{}
@MarthinusEngelbrecht多个重载和条件类型的行为有点古怪,其中有子bug。Me和jcalz偶然发现了不同的行为,这取决于实际的重载,jcalz报告了一个错误:
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
type ReturnTypeWithArgs<T extends (...args: any[]) => any, ARGS_T> =
    Extract<
        T extends { (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3; (...args: infer A4): infer R4; } ? [A1, R1] | [A2, R2] | [A3, R3] | [A4, R4] :
        T extends { (...args: infer A1): infer R1; (...args: infer A2): infer R2; (...args: infer A3): infer R3; } ? [A1, R1] | [A2, R2] | [A3, R3] :
        T extends { (...args: infer A1): infer R1; (...args: infer A2): infer R2; } ? [A1, R1] | [A2, R2] :
        T extends { (...args: infer A1): infer R1; } ? [A1, R1] :
        never,
        [ARGS_T, any]
    >[1]