首页 > 解决方案 > 将基于 Tuple 的类型提取到新的 Tuple

问题描述

假设我有一个字符串元组:

const tuple = ["a", "b", ..., "n"] as const;

映射到某个类型的条目:

type MyType = {
  a: string;
  b: number;
  ...
  n: N;

如何将位于的类型提取Mytype[typeof tuple[number]]到一个新的元组中,结果是:

type MyTypeToTuple = [Mytype[tuple[0]], Mytype[tuple[1]], ... , Mytype[tuple[n]]]

?

标签: typescript

解决方案


You can use mapped types to turn tuples into tuples. Unfortunately, the following straightforward implementation won't work:

type MyTuple = typeof tuple;

type Oops = { [K in keyof MyTuple]: MyType[MyTuple[K]] } // error
// -------------------------------> ~~~~~~~~~~~~~~~~~~
// Type 'readonly ["a", "b", "n"][K]' cannot be used to index type 'MyType'.(2536)
//
/* type Oops = {
    [x: number]: string | number | N;
    readonly 0: string;
    readonly 1: number;
    readonly 2: N;
    length: unknown;
    toString: unknown;
    toLocaleString: unknown; ... */

That's because of a bug or limitation in TypeScript whereby the tuple/array mapping isn't triggered on specific tuple types like MyTuple; see microsoft/TypeScript#27995. For now, you need to do it on an unspecified generic type parameter like T extends MyTuple, and even then the compiler cannot understand that the K in T[K] only includes the numeric indices and therefore that T[K] extends keyof MyType. Thus, instead of MyType[T[K]] you need something like MyType[Extract<T[K], keyof MyType>] with the Extract<T, U> utility type to calm the compiler's worries. Thus we have the two-line definition:

type MapToMyType<T> = { [K in keyof T]: MyType[Extract<T[K], keyof MyType>] };

type MyTypeToTuple = MapToMyType<typeof tuple>
// type MyTypeToTuple = readonly [string, number, /*...*/ N]

Which is now the type you want.

Playground link to code


推荐阅读