我有这样一门课:

type Params = {
    data: string | string[]
}

class Test {
    data: string | string[]

    constructor(params: Params) {
        this.data = this.initData(params.data)
    }

    initData(data: string): string
    initData(data: string[]): string[]
    initData(data: string | string[]): string | string[] {
        if (typeof data === 'string') {
            return data as string
        } else {
            return data as string[]
        }
    }
}

但我得到了这样的错误:

No overload matches this call.
  Overload 1 of 2, '(data: string): string', gave the following error.
    Argument of type 'string | string[]' is not assignable to parameter of type 'string'.
      Type 'string[]' is not assignable to type 'string'.
  Overload 2 of 2, '(data: string[]): string[]', gave the following error.
    Argument of type 'string | string[]' is not assignable to parameter of type 'string[]'.
      Type 'string' is not assignable to type 'string[]'.ts(2769)

Untitled-1(38, 5): The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.

我发现了一个愚蠢的变通办法,它有帮助:

constructor(params: Params) {
    this.data = this.initData(params.data)

    if (typeof params.data === 'string') {
        this.data = this.initData(params.data)
    }
    else {
        this.data = this.initData(params.data)
    }
}

我有点明白为什么这是可行的,但也许有更好的方法来解决这个问题?

推荐答案

microsoft/TypeScript#14107处有一个长期开放的功能请求,该请求支持使用适当的union参数调用overloaded function.即distribute个参数类型联合上的重载解析.目前,它不是语言的一部分,只有变通方法.

如果您现在想要调用一个重载函数,您必须确保该调用适合于它的至少一个调用签名.因此,最常见的解决方法是添加与失败的呼叫对应的缺少的呼叫签名:

class Test {
  data: string | string[]

  constructor(params: Params) {
    this.data = this.initData(params.data)
  }

  // call signatures
  initData(data: string): string
  initData(data: string[]): string[]
  initData(data: string | string[]): string | string[]; // <-- add this

  // implementation
  initData(data: string | string[]): string | string[] {
    return data
  }
}

这在这里很容易做到,因为您只需要再添加一个调用签名.但是,如果您一开始就有𝒏调用签名,则将它们的每一个可能集合组合在一起将产生类似2𝒏的调用签名.有了𝒏,这一数字迅速增长了very%.为了避免这种指数增长,您可以只使用一个使用a distributive conditional type计算返回类型的generic调用签名:

class Test {
  data: string | string[]

  constructor(params: Params) {
    this.data = this.initData(params.data)
  }

  // call signature
  initData<T extends string | string[]>(data: T): 
    T extends string ? string :
    T extends string[] ? string[] :
    never;

  // implementation
  initData(data: string | string[]): string | string[] {
    return data
  }
}

现在,单个调用签名可以自动分布在联合上,并且它随着原始调用签名的数量线性扩展.但是,作为重载,它仍然是implemented,因为编译器当前不能验证返回泛型条件类型的函数体.有关这方面的讨论,请参见microsoft/TypeScript#33912.因此,我们在这里使用重载函数实现的"松散性"来避免类型断言.

Playground link to code

Typescript相关问答推荐

相同端点具有不同响应时的RTKQuery类型定义

在泛型类型中对子代执行递归时出错

如何缩小变量访问的对象属性范围?

React重定向参数

如何重构长联合类型?

在函数中打字推断记录的关键字

如何调整对象 struct 复杂的脚本函数的泛型类型?

我可以使用TypeScrip从字符串词典/记录中填充强类型的环境对象吗?

接口重载方法顺序重要吗?

使用Redux Saga操作通道对操作进行排序不起作用

为什么我在这个重载函数中需要`as any`?

ANGLE订阅要么不更新价值,要么不识别价值变化

TYPE FOR ARRAY,其中每个泛型元素是单独的键类型对,然后在该数组上循环

API文件夹内的工作员getAuth()帮助器返回:{UserID:空}

如何在Angular 服务中制作同步方法?

为什么发送空字段?

如何创建内部和外部组件都作为props 传入的包装器组件?

参数未映射泛型返回类型

如何使用 runInInjectionContext 中的参数测试功能性路由防护

TS2532:对象可能未定义