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

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

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

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

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


person Pedro A    schedule 24.08.2020    source источник


Ответы (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>;

площадка Link

Изменить

Более простая версия, любезность 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>;

площадка Link

/Изменить

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

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

площадка Link

Просто для удовольствия, 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>;

площадка Link

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

person Titian Cernicova-Dragomir    schedule 24.08.2020
comment
Блин, ты первый пришел. настоящий рекурсивный условный тип для 4.1! - person jcalz; 24.08.2020
comment
@jcalz Я искал такой ответ ????. Это захватывающее время в системе типов ts ???? - person Titian Cernicova-Dragomir; 24.08.2020
comment
Я мог бы пойти с type Flatten<T> = T extends [any, ...infer R] ? [...T[0], ...Flatten<R>] : [] сам, но этот тоже работает - person jcalz; 24.08.2020
comment
@jcalz добавил и эту версию, вы правы, она проще :) - person Titian Cernicova-Dragomir; 24.08.2020
comment
Потрясающе, спасибо! Какая минимальная версия ТС для работы хака? Это 4.0 из-за [...A, ...B]? - person Pedro A; 25.08.2020
comment
@PedroA версия выше да, требуется 4.0. Вероятно, вы можете написать вспомогательный тип для объединения кортежей для эквивалентного эффекта. Но я бы рекомендовал использовать 4.0, если это возможно. - person Titian Cernicova-Dragomir; 25.08.2020
comment
@TitianCernicova-Драгомир Хорошо!! Я уже использую 4.0, так что все в порядке, я спросил из любопытства. В целях обучения, не могли бы вы показать, как сделать этот помощник, который вы сказали для ‹ 4.0? Некоторое время я пытался придумать способ (прежде чем спросить здесь, на SO), но потерпел неудачу... - person Pedro A; 25.08.2020
comment
Привет @jcalz и @TitanCernicova-Dragomir, на самом деле я решил использовать это решение сейчас, но я получаю ошибку TS2574: тип остаточного элемента должен быть типом массива в части ...infer R. Я использую TypeScript 4.0.2, что происходит? - person Pedro A; 25.08.2020
comment
Можете ли вы предоставить минимально воспроизводимый пример этой ошибки? Желательно со ссылкой на веб-IDE, например Игровая площадка TS? Иначе трудно сказать, что происходит - person jcalz; 25.08.2020
comment
@jcalz Я понял это. Это проблема с Prettier. Он меняет ...infer R на ...(infer R) даже в новой версии 2.1.0. Сейчас я использую комментарий // prettier-ignore, чтобы отключить это. Всем спасибо за внимание! :) - person Pedro A; 25.08.2020