Skip to content

TypeScript 中的重载函数类型



本文其实主要回答了三个问题:

重载函数是如何定义的呢?
如何获取重载函数的返回类型?
又如何获取重载函数的参数类型呢?

我们先定义一个简单的重载函数:

typescript
function fn(): void;
function fn(a: string): string;
function fn(a: number): number;
function fn<T extends 'a' | 'b'>(a: T): T;
function fn<T extends { a: string }>(a: T): T;
function fn(a: string, b: number): [string, number];
function fn(a?: any) {
  return a;
}

然后尝试获取函数的参数类型:

typescript
type ParametersFn = Parameters<typeof fn>;
type ReturnTypeFn = ReturnType<typeof fn>;

此时我们发现, 只有最后一个 overload 签名的参数类型和返回值类型被返回了...

那到底如何才能正确获取重载函数的所有参数类型和返回值类型呢?

首先, 我们来研究一下如何正确的定义一个重载函数类型, 参考 TypeScript handbook 中 的Call Signatures, 我们很容易写出上面的重载函数的类型:

typescript
type F = {
  (): void;
  (a: string): string;
  (a: number): number;
  <T extends 'a' | 'b'>(a: T): T;
  <T extends { a: string }>(a: T): T;
  (a: string, b: number): [string, number];
};

接着, 试着写出一个OverloadReturnType<T>来获取返回值类型, 但是问题在于 overload 签名的数量不是固定的, 如何来进行定义呢? 这里只能采用一个折中的写法, 把每个方法 签名类型转换到一个 Tuple 类型中, 以下写法最多可以支持 8 个函数重载签名:

typescript
type Fn = (...args: any) => any;
type OverloadReturns<F extends Fn> = ReturnType<OverloadSignatures<F>>;
type OverloadParameters<F extends Fn> = Parameters<OverloadSignatures<F>>;
type MatchOverload<F extends Fn, P extends Fn> = Extract<OverloadSignatures<F>, P>;
type OverloadSignatures<F extends Fn> = OverloadsToTuple<F>[number];
type OverloadsToTuple<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: P1) => R1,
      (...args: P2) => R2,
      (...args: P3) => R3,
      (...args: P4) => R4,
      (...args: P5) => R5,
      (...args: P6) => R6,
      (...args: P7) => R7,
      (...args: P8) => R8
    ]
  : 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: P1) => R1, (...args: P2) => R2, (...args: P3) => R3, (...args: P4) => R4, (...args: P5) => R5, (...args: P6) => R6, (...args: P7) => R7]
  : 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: P1) => R1, (...args: P2) => R2, (...args: P3) => R3, (...args: P4) => R4, (...args: P5) => R5, (...args: P6) => R6]
  : 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: P1) => R1, (...args: P2) => R2, (...args: P3) => R3, (...args: P4) => R4, (...args: P5) => R5]
  : T extends { (...args: infer P1): infer R1; (...args: infer P2): infer R2; (...args: infer P3): infer R3; (...args: infer P4): infer R4 }
  ? [(...args: P1) => R1, (...args: P2) => R2, (...args: P3) => R3, (...args: P4) => R4]
  : T extends { (...args: infer P1): infer R1; (...args: infer P2): infer R2; (...args: infer P3): infer R3 }
  ? [(...args: P1) => R1, (...args: P2) => R2, (...args: P3) => R3]
  : T extends { (...args: infer P1): infer R1; (...args: infer P2): infer R2 }
  ? [(...args: P1) => R1, (...args: P2) => R2]
  : T extends { (...args: infer P1): infer R1 }
  ? [(...args: P1) => R1]
  : never;

RUN ME 尝试着使用一下, 结果非常完美!

image.png

Released under the MIT License.