TypeScript中的重载函数类型

184 阅读3分钟

TypeScript中的重载函数类型



原文: web3c.work/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;
}

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

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

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

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

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

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个函数重载签名:

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