首页 > 解决方案 > TypeScript 构建类似 Promise.all 的函数 - 重载签名与函数实现不兼容

问题描述

通过使用 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;
}

[工作]似乎唯一可行的方法是使用.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

解决方案


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

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

  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;
}

推荐阅读