输入重载函数的类型推断

Rém*_*let 6 typescript

考虑以下重载函数:

function foo(arg1: string, cb: (err: Error|null, res: string) => void): void
function foo(arg1: string, arg2: string, cb: (err: Error|null, res: string) => void): void
Run Code Online (Sandbox Code Playgroud)

我想promisify使用这些功能.但默认实现返回无效类型.

当它回来

(arg1: string, arg2: string) => Promise<{}>
Run Code Online (Sandbox Code Playgroud)

我希望它会回归

{
   (arg1: string): Promise<string>;
   (arg1: string, arg2: string): Promise<string>;
}
Run Code Online (Sandbox Code Playgroud)

考虑到这一点,我想解决这些问题.我设法使用以下内容覆盖该特定原型:

export type Callback<T> = (err: Error | null, reply: T) => void;
export type Promisify<T> =

    T extends {
        (arg1: infer T1, cb?:  Callback<infer U>): void;
        (arg1: infer P1, arg2: infer P2, cb?:  Callback<infer U2>): void;
    } ? {
        (arg1: T1): Promise<U>;
        (arg1: P1, arg2: P2): Promise<U2>;
    } :

    T extends (cb?:  Callback<infer U>) => void ? () => Promise<U> :
    T extends (arg1: infer T1, cb?:  Callback<infer P>) => void ? (arg1: T1) => Promise<P> :
    T extends (arg1: infer T1, arg2: infer T2, cb?:  Callback<infer U>) => void ? (arg1: T1, arg2: T2) => Promise<U> :
    T extends (arg1: infer T1, arg2: infer T2, arg3: infer T3, cb?:  Callback<infer U>) => void ? (arg1: T1, arg2: T2, arg3: T3) => Promise<U> :
    T extends (arg1: infer T1, arg2: infer T2, arg3: infer T3, arg4: infer T4, cb?:  Callback<infer U>) => void ? (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<U> :
    T;
Run Code Online (Sandbox Code Playgroud)

但它需要我专门列出所有潜在的方法重载.

它有一种方法可以立即转换所有方法的重载,类似于我们如何转换对象属性?

Tit*_*mir 12

我们可以在休息参数和扩展表达式中使用元组的3.0特性来获得重载参数的并集,但是我们需要为函数具有的每个重载数添加一个大小写:

export type GetOverloadArgs<T> = 
    T extends { (...o: infer U) : void, (...o: infer U2) : void, (...o: infer U3) : void   } ? U | U2 | U3:
    T extends { (...o: infer U) : void, (...o: infer U2) : void  } ? U | U2 :
    T extends { (...o: infer U) : void } ? U : never
Run Code Online (Sandbox Code Playgroud)

所以例如foo

type fooParams = GetOverloadArgs<typeof foo> 
// will be 
type fooParams = [string, (err: Error | null, res: string) => void] | [string, string, (err: Error | null, res: string) => void]
Run Code Online (Sandbox Code Playgroud)

从这里我们可以使用类似于你的类型Promisify为union中的每个参数集创建一个函数:

export type PromisifyOne<T extends any[]> =
    T extends [Callback<infer U>?] ? () => Promise<U> :
    T extends [infer T1, Callback<infer P>] ? (arg1: T1) => Promise<P> :
    T extends [infer T1, infer T2, Callback<infer U>?] ? (arg1: T1, arg2: T2) => Promise<U> :
    T extends [infer T1, infer T2, infer T3, Callback<infer U>?]? (arg1: T1, arg2: T2, arg3: T3) => Promise<U> :
    T extends [infer T1, infer T2, infer T3, infer T4, Callback<infer U>?] ? (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<U> :
    T;
Run Code Online (Sandbox Code Playgroud)

使用条件类型的分布行为,我们可以创建所有重载的并集:

export type Promisify<T> =PromisifyOne<GetOverloadArgs<T>> 
export type Promisify<T> =PromisifyOne<GetOverloadArgs<T>> 
type fooOverloadUnion = Promisify<typeof foo>
// Same as 
type fooOverloadUnion = ((arg1: string) => Promise<string>) | ((arg1: string, arg2: string) => Promise<string>)
Run Code Online (Sandbox Code Playgroud)

为了再次使这个可调用,我们可以使用将union转换为交集,使用UnionToIntersection,最终结果为:

export type Callback<T> = (err: Error | null, reply: T) => void;
export type PromisifyOne<T extends any[]> =
    T extends [Callback<infer U>?] ? () => Promise<U> :
    T extends [infer T1, Callback<infer P>?] ? (arg1: T1) => Promise<P> :
    T extends [infer T1, infer T2, Callback<infer U>?] ? (arg1: T1, arg2: T2) => Promise<U> :
    T extends [infer T1, infer T2, infer T3, Callback<infer U>?]? (arg1: T1, arg2: T2, arg3: T3) => Promise<U> :
    T extends [infer T1, infer T2, infer T3, infer T4, Callback<infer U>?] ? (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => Promise<U> :
    T;

export type GetOverloadArgs<T> = 
    T extends { (...o: infer U) : void, (...o: infer U2) : void, (...o: infer U3) : void   } ? U | U2 | U3:
    T extends { (...o: infer U) : void, (...o: infer U2) : void  } ? U | U2 :
    T extends { (...o: infer U) : void } ? U : never

type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never
export type Promisify<T> =  UnionToIntersection<PromisifyOne<GetOverloadArgs<T>>>

// Sample
declare function foo(arg1: string, cb: (err: Error|null, res: string) => void): void
declare function foo(arg1: string, arg2: string, cb: (err: Error|null, res: string) => void): void

declare const  fooPromise: Promisify<typeof foo>
let r = fooPromise("")
let r2 = fooPromise("", "")
Run Code Online (Sandbox Code Playgroud)