Завдання

У цьому завданні ви повинні реалізувати тип Zip<T, U>, де T і U мають бути Tuple:

// expected to be [[1, true], [2, false]]
type R = Zip<[1, 2], [true, false]>;

Розв’язок

Почнемо з порожнього типу, який ми будемо використовувати для реалізації. Тип-параметр T використовується, щоб отримати перший кортеж, який нам потрібно з’єднати, а U - другий:

type Zip<T, U> = any;

Перш ніж приступити до реалізації, дозвольте мені навести вам приклад того, що тут означає з’єднати. Наприклад, якщо у вас є кортеж [1, 2] і кортеж [true, false], вам потрібно об’єднати перші елементи кортежу в новий кортеж - [1, true]. Потім зробити те ж саме, але з другими - [2, false]. Зрештою, помістіть ці кортежі в інший кортеж - [[1, true], [2, false]]. Ось що означає з’єднати.

Як бачите, нам потрібно мати можливість отримати перший елемент кортежу. Ми можемо зробити це за допомогою виведення! Візьмемо перший кортеж T і виведемо з нього елемент (TI) та хвіст (TT):

type Zip<T, U> = T extends [infer TI, ...infer TT] ? never : never;

Але у нас є ще один кортеж, який ми не врахували. Тож робимо те саме для U — виводимо елемент (UI) й хвіст (UT):

type Zip<T, U> = T extends [infer TI, ...infer TT]
  ? U extends [infer UI, ...infer UT]
    ? never
    : never
  : never;

Якщо в обох кортежах є елемент і хвіст, ми можемо з’єднати їх разом. Для цього ми повертаємо кортеж із TI і UI:

type Zip<T, U> = T extends [infer TI, ...infer TT]
  ? U extends [infer UI, ...infer UT]
    ? [TI, UI]
    : never
  : never;

Проблема в тому, що ми не обробляємо інші елементи. Ми просто отримуємо один кортеж, і все. Щоб вирішити це, нам потрібно знову викликати Zip з хвостами кортежів. Крім того, не забувайте, що нам потрібен кортеж з кортежів, тому ми знову загортаємо його в квадратні дужки:

type Zip<T, U> = T extends [infer TI, ...infer TT]
  ? U extends [infer UI, ...infer UT]
    ? [[TI, UI], Zip<TT, UT>]
    : never
  : never;

Добре мати тип, який можна викликати рекурсивно. Але використовуючи тип Zip, ми отримуємо в кінці кортеж кортежів, який опиняється всередині нашого кортежу кортежів. Нам це не потрібно. Тож розгортаємо результат виклику Zip:

type Zip<T, U> = T extends [infer TI, ...infer TT]
  ? U extends [infer UI, ...infer UT]
    ? [[TI, UI], ...Zip<TT, UT>]
    : never
  : never;

Останнє питання, на яке потрібно відповісти - що робити, коли хвоста більше немає? Замість того, щоб повертати тип never, ми можемо повернути лише порожній кортеж, щоб не зламати рекурсивний виклик.

type Zip<T, U> = T extends [infer TI, ...infer TT]
  ? U extends [infer UI, ...infer UT]
    ? [[TI, UI], ...Zip<TT, UT>]
    : []
  : [];

Посилання