【TS】如何在 typescript 中将数组字面量推断到 tuple

在 ts 中推断到 tuple 的实践方案

Tuple 保留了数组内容的更多信息,包括数组长度,以及每个元素的类型(尤其是字面量类型)和元素之间的相对位置。

尝试一下 实现 Promise.all 的类型。

一个比较优秀的实现是:

declare function PromiseAll<A extends any[]>(values: readonly [...A]): Promise<{
  [key in keyof A]: Awaited<A[key]>
}>

其中 [...A] 用于显式将参数类型推断为 tuple,而不是更宽的数组类型 type[]

不过,typescript 标准库里是这样实现的:

/**
 * Creates a Promise that is resolved with an array of results when all of the provided Promises
 * resolve, or rejected when any Promise is rejected.
 * @param values An array of Promises.
 * @returns A new Promise.
 */
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;

注意到它没有使用 [...T],而是将泛型 T 的约束变成了一个 union type ... | [],追加了一个空 tuple 类型。

而当类型中包含 tuple 时,编译器会先一步**推断(inference)该类型为 tuple,尽管在下面这个例子中,参数的类型在可分配性(assignability)**上存在问题,但参数仍然被推断为 tuple。

declare function PromiseAll<T extends (readonly unknown[]) | []>(values: T): Promise<{
  -readonly [key in keyof T]: Awaited<T[key]>
}>

const promiseAllTest3 = PromiseAll([1, 2, 3]) // Promise<[number, number, number]>

如果我们去掉这个导致 tuple 推断的 ... | [],我们会观察到,参数失去了 tuple 的性质,变为了更宽的 number[] 类型。

实际上追加的 tuple 类型是什么并无所谓,即便使用 ... | [string],在上面的 case 中也是 work 的。

declare function PromiseAll<T extends readonly unknown[]>(values: T): Promise<{
  -readonly [key in keyof T]: Awaited<T[key]>
}>

const promiseAllTest3 = PromiseAll([1, 2, 3]) // Promise

所以,作为参数的接收方,我们现在拥有了两个方案来让 ts 编译器将字面量推断到 tuple。

  • 使用 spread operator。
  • 使用任意 tuple 字面量作为泛型约束,必要时可以使用 union type 来绕过可分配性检查。

ref

type-challenges/promise-all

stackoverflow#74848194

你可能感兴趣的:(Web,JS,typescript,javascript,开发语言)