Как получить тип сглаженного кортежа из набора кортежей?

#typescript #tuples #recursive-type

#typescript #кортежи #recursive-type

Вопрос:

Предположим, у меня есть кортеж кортежей:

 type Example = [[3,5,7], [4,9], [0,1,10,9]];
  

Я хочу создать тип утилиты, Flatten<T> такой Flatten<Example> , который дает:

 type FlatExample = Flatten<Example>;
// type FlatExample = [3,5,7,4,9,0,1,10,9];
  

Для моего варианта использования вы можете предположить, что кортеж вложен только на один уровень глубиной. Кортежи могут иметь любой размер.

Как я могу это сделать?

Ответ №1:

Для этого вам потребуются рекурсивные условные типы. Это будет полностью поддерживаться в 4.1:

 type Example = [[3,5,7], [4,9], [0,1,10,9]];
type Flatten<T extends any[]> = 
    T extends [infer U, ...infer R] ? U extends any[] ? [...U, ... Flatten<R>]: []: []
type FlatExample = Flatten<Example>;
  

Ссылка на игровую площадку

Редактировать

Более простая версия, краткое изложение jcalz:

 type Example = [[3,5,7], [4,9], [0,1,10,9]];
type Flatten<T extends any[]> = 
    T extends [any, ...infer R] ? [...T[0], ... Flatten<R>]:  []
type FlatExample = Flatten<Example>;
  

Ссылка на игровую площадку

/Редактировать

Вы можете взломать версию даже сегодня (дополнительная косвенность необходима, чтобы обмануть компилятор и заставить его разрешить рекурсивный условный тип, концептуально это эквивалентно более простой приведенной выше версии):

 type Example = [[3, 5, 7], [4, 9], [0, 1, 10, 9]];
type Flatten<T extends any[]> = T extends [infer U, ...infer R] ? {
    1: U extends any[] ? [...U, ...Flatten<R>] : [],
    2: []
}[U extends any[] ? 1 : 2] : [];

type FlatExample = Flatten<Example>;

  

Ссылка на игровую площадку

Просто для развлечения, версия 4.1 с обобщенным уплощением.

 type Example = [[3,5,7], [4,9, [10, 12, [10, 12]]], [0,1,10,9, [10, 12]]];
type Flatten<T extends any[]> = 
    T extends [infer U, ...infer R] ? 
        U extends any[] ? 
        [...Flatten<U>, ... Flatten<R>]: [U, ... Flatten<R>]: []
type FlatExample = Flatten<Example>;
  

Ссылка на игровую площадку

Примечание: Хотя рекурсивные типы более поддерживаются в 4.1, вы все равно можете столкнуться с жестко заданными ограничениями компилятора, такими как глубина создания экземпляра типа и общее количество экземпляров типа (поскольку такие рекурсивные типы генерируют много экземпляров типа). Поэтому используйте экономно.

Комментарии:

1. черт возьми, ты добрался сюда первым. подлинный рекурсивный условный тип для 4.1!

2. @jcalz Я долго искал такой ответ 😅. Это захватывающее время в системе ts type system 😉

3. Я мог бы использовать type Flatten<T> = T extends [any, ...infer R] ? [...T[0], ...Flatten<R>] : [] себя, но этот тоже работает

4. @jcalz добавил и эту версию, вы правы, она проще 🙂

5. @jcalz Я понял это. Это проблема с Prettier. Он меняется ...infer R на ...(infer R) даже в новой версии 2.1.0. Я использую // prettier-ignore комментарий сейчас, чтобы отключить это. Спасибо за все внимание! 🙂