【问题标题】:TypeScript Building a Promise.all like function - Overload signature is not compatible with function implementationTypeScript 构建类似 Promise.all 的函数 - 重载签名与函数实现不兼容
【发布时间】:2019-04-20 22:17:33
【问题描述】:

通过使用TypeScript,任何使用sequentially的人都应该被强制传递1到N个参数,输入Promise<T>等于输出[Promise<T>,..

[不工作]我在以下列方式重载第一个声明时遇到 tslint 错误:

重载签名与函数实现不兼容。 ts(2394)

promise.ts

function sequentially<T1>(promiseWrappers: [
  () => Promise<T1>
]): Promise<[T1]>;
async function sequentially<T1, T2>(promiseWrappers: [
  () => Promise<T1>,
  () => Promise<T2> 
]): Promise<[T1, T2]> {
  const resolved = [];
  for (const wrapper of promiseWrappers) {
    resolved.push(await wrapper());
  }
  // @ts-ignore
  return resolved;
}

[Working]似乎唯一可行的方法是使用.js 和单独的声明文件.d.ts 编写实现,如下所示:

promise.js

export async function sequentially(promiseWrappers) {
  const resolved = [];
  for (const wrapper of promiseWrappers) {
    resolved.push(await wrapper());
  }
  return resolved;
}

promise.d.ts

export declare function sequentially<T1, T2>(promiseWrappers: [
  () => Promise<T1>,
  () => Promise<T2>
]): Promise<[T1, T2]>;

export declare function sequentially<T1, T2, T3>(promiseWrappers: [
  () => Promise<T1>,
  () => Promise<T2>,
  () => Promise<T3>
]): Promise<[T1, T2, T3]>;

如何解决第一个 TS 实现案例?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    在 TypeScript 中实现重载函数有一些规则:

    1. 调用站点不考虑实现签名,它仅用于对实现进行类型检查(在记录overloads in the handbook的段落末尾提到)

    2. 实现签名必须具有与所有重载签名声明兼容的参数。实际上,这意味着每个参数类型必须是该参数位置的所有重载的所有参数类型的联合类型。

    3. 实现签名必须具有返回类型,该类型是所有重载的返回类型的交集。实际上,这意味着实现必须在每个 return 语句中对该类型进行类型转换,或者只是将实现返回类型声明为 any。

    我在任何地方都找不到 2 和 3 的文档,但它们遵循函数类型兼容性的一般差异规则以及实现签名必须与所有重载声明兼容这一事实。

    这里是代码,在--strictFunctionTypes--noImplicitAny开启的情况下编译

    function sequentially<T1>(promiseWrappers: [
        () => Promise<T1>
    ]): Promise<[T1]>;
    function sequentially<T1, T2>(promiseWrappers: [
        () => Promise<T1>,
        () => Promise<T2>,
    ]): Promise<[T1, T2]>;
    async function sequentially<T1, T2>(promiseWrappers: [
      () => Promise<T1>,
    ] | [
      () => Promise<T1>,
      () => Promise<T2> 
    ]): Promise<[T1, T2] & [T1]> {
      const resolved = [] as unknown as [T1, T2] & [T1];
      for (const wrapper of promiseWrappers) {
        resolved.push(await wrapper());
      }
      return resolved;
    }
    

    【讨论】:

      【解决方案2】:

      您可能想要支持的重载可能彼此无关。为了支持这一点,让所有any 类型的最底层签名。像这样的东西应该适合你。

      function sequentially<T1>(promiseWrappers: [
        () => Promise<T1>
      ]): Promise<[T1]>;
      function sequentially<T1, T2>(promiseWrappers: [
        () => Promise<T1>,
        () => Promise<T2> 
      ]): Promise<[T1, T2]>;
      async function sequentially(promiseWrappers: any[]): Promise<any[]> {
        const resolved = [];
        for (const wrapper of promiseWrappers) {
          resolved.push(await wrapper());
        }
        // @ts-ignore
        return resolved;
      }
      

      【讨论】:

      • 感谢您的回答。虽然 tslint 错误会以这种方式消失,但该函数现在有 3 个不同的签名。更广泛的any 匹配所有内容。因此,类型是没有意义的。
      • @zurfyx 我认为如果您没有打开任何隐含性,那么毫无意义可能是夸大其词......但是是的,从技术上讲,另一个答案更正确。您需要在函数实现中将| 可能的参数一起。我建议尽可能地提升类型以简化代码。
      • 你是完全正确的@Catalyst +1(只是接受另一个答案,因为它更完整)
      猜你喜欢
      • 2017-01-17
      • 1970-01-01
      • 1970-01-01
      • 2019-03-19
      • 1970-01-01
      • 2019-08-12
      • 2019-09-18
      • 2018-09-14
      • 1970-01-01
      相关资源
      最近更新 更多