🐥

[TypeScript]10個までのオーバーロードされた関数型から引数の型や返り値の型を共用体で取り出す方法

2022/09/18に公開

こちらの記事を参考にしています。

https://zenn.dev/uhyo/articles/typescript-overload-infer

10個までのオーバーロードに対応した戻り値の型を取得

10個までのオーバーロードに対応した戻り値型を返却するコードを作成してみました。

type OverloadReturnType<T> = T extends
  {
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
    (...args: any): infer R,
  }
  ? R : never

// 以下、使い方

const niceFunc: {
  (arg: string): { [key: string]: string };
  (arg: boolean): boolean;
  (arg: number): string;
  (arg: string): number;
} = (arg) => {
  return arg as never //dummy
}

type Func = typeof niceFunc;

// number
type Ret = ReturnType<Func>;
// string | number | boolean | {[key: string]: string;}
type Ret2 = OverloadReturnType<Func>;

Ret2string | number | boolean | {[key: string]: string;}になっています

ちなみに同様方法で引数型を取り出そうとすると交差型で取得してしまうので、使いどころの無い状態になりました。引数まで本気でやろうとすると元記事のuhyoさんがおっしゃるとおり力業しかないようです。

10個までのオーバーロードに対応したパラメータ型を取得する

これが力業です。

type OverloadFunction1<T> = T extends
    {
        (...args: infer P1): infer R1,
    }
    ? [P1, R1] : never

type OverloadFunction2<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
    }
    ? [P1, R1] | [P2, R2] : never

type OverloadFunction3<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] : never

type OverloadFunction4<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
        (...args: infer P4): infer R4,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] | [P4, R4] : never

type OverloadFunction5<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
        (...args: infer P4): infer R4,
        (...args: infer P5): infer R5,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] | [P4, R4] | [P5, R5] : never

type OverloadFunction6<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
        (...args: infer P4): infer R4,
        (...args: infer P5): infer R5,
        (...args: infer P6): infer R6,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] | [P4, R4] | [P5, R5] | [P6, R6] : never

type OverloadFunction7<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
        (...args: infer P4): infer R4,
        (...args: infer P5): infer R5,
        (...args: infer P6): infer R6,
        (...args: infer P7): infer R7,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] | [P4, R4] | [P5, R5] | [P6, R6] | [P7, R7] : never

type OverloadFunction8<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
        (...args: infer P4): infer R4,
        (...args: infer P5): infer R5,
        (...args: infer P6): infer R6,
        (...args: infer P7): infer R7,
        (...args: infer P8): infer R8,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] | [P4, R4] | [P5, R5] | [P6, R6] | [P7, R7] | [P8, R8] : never

type OverloadFunction9<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
        (...args: infer P4): infer R4,
        (...args: infer P5): infer R5,
        (...args: infer P6): infer R6,
        (...args: infer P7): infer R7,
        (...args: infer P8): infer R8,
        (...args: infer P9): infer R9,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] | [P4, R4] | [P5, R5] | [P6, R6] | [P7, R7] | [P8, R8] | [P9, R9] : never



type OverloadFunction10<T> = T extends
    {
        (...args: infer P1): infer R1,
        (...args: infer P2): infer R2,
        (...args: infer P3): infer R3,
        (...args: infer P4): infer R4,
        (...args: infer P5): infer R5,
        (...args: infer P6): infer R6,
        (...args: infer P7): infer R7,
        (...args: infer P8): infer R8,
        (...args: infer P9): infer R9,
        (...args: infer P10): infer R10,
    }
    ? [P1, R1] | [P2, R2] | [P3, R3] | [P4, R4] | [P5, R5] | [P6, R6] | [P7, R7] | [P8, R8] | [P9, R9] | [P10, R10] : never



type OverloadFunction<T> =
    any extends OverloadFunction10<T> ? OverloadFunction10<T> :
    any extends OverloadFunction9<T> ? OverloadFunction9<T> :
    any extends OverloadFunction8<T> ? OverloadFunction8<T> :
    any extends OverloadFunction7<T> ? OverloadFunction7<T> :
    any extends OverloadFunction6<T> ? OverloadFunction6<T> :
    any extends OverloadFunction5<T> ? OverloadFunction5<T> :
    any extends OverloadFunction4<T> ? OverloadFunction4<T> :
    any extends OverloadFunction3<T> ? OverloadFunction3<T> :
    any extends OverloadFunction2<T> ? OverloadFunction2<T> :
    any extends OverloadFunction1<T> ? OverloadFunction1<T> :
    never

type OverloadParameters<T> = OverloadFunction<T>[0]

const niceFunc: {
    (arg: string): { [key: string]: string };
    (arg: boolean): boolean;
    (arg: number): string;
    (arg: string): number;
} = (arg) => {
    return arg as never //dummy
}

type Func = typeof niceFunc;

//[[arg: string], {[key: string]: string;}] | [[arg: boolean], boolean] | [[arg: number], string] | [[arg: string], number]
type FunctionType = OverloadFunction<Func>
//[arg: string]
type Params1 = Parameters<Func>
//[arg: string] | [arg: boolean] | [arg: number] | [arg: string]
type Params2 = OverloadParameters<Func>

ということで取り出しに成功しました。Params2[arg: string] | [arg: boolean] | [arg: number] | [arg: string]という状態になっています。
OverloadFunctionの方はパラメータと戻り値の対応関係もそのまま含まれています。

まとめ

力業は思いつきでざっと実装してみただけなので、もっとエレガントに書く方法があるかもしれません。

GitHubで編集を提案

Discussion