typescript - 更干净的功能重载,无需复制不改变的部分

标签 typescript typescript-generics

type Foo = {
    (trx: Transaction | undefined, event: SomeEnum.OPT_1, params: { whatever: number }): Promise<void>;
    (trx: Transaction | undefined, event: SomeEnum.OPT_2, params: { whatever: string }): Promise<void>;
};

export const foo: Foo = async (trx: Transaction | undefined, event: SomeEnum, params: object | undefined) => {};

我正在尝试函数重载,上面的代码可以工作,但在我的现实情况下,我将有大约 40 个重载,其中只有一些参数会有所不同,所以我想知道,有没有办法改进代码,这样我就不必一遍又一遍地复制所有不变的元素?在示例中,第一个参数 trx 始终相同,函数的返回类型也始终相同。我尝试过这样的东西,但 typescript 显然不喜欢它:

type FooPartial<T1 extends SomeEnum, T2> = (trx: Transaction | undefined, event: T1, params: T2) => Promise<void>;

type Foo {
    FooPartial<SomeEnum.OPT_1, { whatever: number }>
    FooPartial<SomeEnum.OPT_2, { whatever: string }>
}

最佳答案

您需要进行动态重载。

为了做到这一点,您需要:

  1. 计算循环中的每个重载

type Transaction = {
    tag: 'Transaction'
}

type Maps = {
    [SomeEnum.OPT_1]: string;
    [SomeEnum.OPT_2]: number
}


type Overloading = {
    [Ev in keyof Maps]:  (trx: Transaction | undefined, event: Ev, params: Maps[Ev]) => Promise<void>
}
  • 现在我们应该只获取Overloading类型的值
  • type Values<T> = T[keyof T]
    
    type Overloading = Values<{
        [Ev in keyof Maps]:  (trx: Transaction | undefined, event: Ev, params: Maps[Ev]) => Promise<void>
    }>
    
  • 现在,当我们拥有所有允许的函数的并集时,我们需要将此并集转换为重载函数。为此,您只需使用交集 &
  • // credits goes to https://stackoverflow.com/a/50375286
    type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
      k: infer I
    ) => void
      ? I
      : never;
    
    type Overloading =UnionToIntersection<Values<{
        [Ev in keyof Maps]:  (trx: Transaction | undefined, event: Ev, params: Maps[Ev]) => Promise<void>
    }>>
    

    完整代码

    
    
    enum SomeEnum {
        OPT_1 = '1',
        OPT_2 = '2'
    }
    
    type Transaction = {
        tag: 'Transaction'
    }
    
    type Maps = {
        [SomeEnum.OPT_1]: string;
        [SomeEnum.OPT_2]: number
    }
    
    type Values<T> = T[keyof T]
    
    
    // credits goes to https://stackoverflow.com/a/50375286
    type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
      k: infer I
    ) => void
      ? I
      : never;
    
    type Overloading =UnionToIntersection<Values<{
        [Ev in keyof Maps]:  (trx: Transaction | undefined, event: Ev, params: Maps[Ev]) => Promise<void>
    }>>
    
    export const foo: Overloading = async (trx: Transaction | undefined, event: SomeEnum, params: Values<Maps>) => { };
    
    foo({tag:'Transaction'}, SomeEnum.OPT_1, 'string') // ok
    

    Playground

    Here ,在我的博客中,你可以找到类似的解决方案

    关于typescript - 更干净的功能重载,无需复制不改变的部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68365226/

    相关文章:

    reactjs - 如何正确使用 Typescript 中的 React.lazy() 导入带有泛型类型参数的 React 组件?

    javascript - 类型错误 : Cannot read property 'form' of null angular

    javascript - Typescript 声明的静态类方法不是函数

    typescript - 如何在 Typescript 上对未知类型使用条件

    typescript - 你能为 TypeScript 泛型指定多个类型约束吗

    typescript - 将对象文字分配给 typescript 通用类型

    typescript - 使 typescript 函数返回类型以输入为条件

    javascript - typescript 推断函数参数联合

    typescript 编译器错误 "Generic type requires 0 type argument(s)"

    Angular 未捕获类型错误 : e is not a constructor after build --prod (work on ng serve)