TypeScript - добавить тип в конец типа кортежа - PullRequest
1 голос
/ 01 апреля 2020

Можно ли написать помощника, который будет добавлять дополнительный тип к данному типу кортежа?

type A = Append<['a', 'b'], 'c'> // resulting in ['a', 'b', 'c']

Мне также было бы интересно объединить 2 типа кортежей:

type B = Join<['a', 'b'], ['c', 'd']> // resulting in ['a', 'b', 'c', 'd']

1 Ответ

2 голосов
/ 01 апреля 2020

В TS довольно просто написать функции, которые будут сдвигать и снимать элементы из кортежа. Подумайте:

type Shift<T extends Array<any>> 
= ((...a: T) => any) extends ((a: any, ...result: infer Result) => any) 
? Result 
  : never;

type Unshift<A, T extends Array<any>> 
= ((a: A, ...b: T) => any) extends ((...result: infer Result) => any) 
? Result 
  : never;

type A = Shift<['a', 'b', 'c']> // A is ['b','c']
type B = Unshift<'a', A> // B is again ['a', 'b', 'c']

Но вы спросили о другом способе обхода, это более сложно, но при использовании выше Shift и Unshift это выполнимо. Обратите внимание:

type Append<
  T extends any[]
  , A
  , ReducedT extends any[] = T
  , Result extends any[] = [A]
  , Rest extends any[] = Shift<ReducedT>
  , Last extends T[keyof T] = T[Rest['length']]
  > = {
  [K in keyof ReducedT]: Rest['length'] extends 0 ? Unshift<Last, Result> : Append<T, A, Rest, Unshift<Last, Result>> 
}[0]

type R = Append<['a', 'b', 'c'], 'd'>
// ['a', 'b', 'c', 'd']

type Merge<
  A extends any[]
  , B extends any[]
  , ReducedA extends any[] = A
  , Result extends any[] = B
  , Rest extends any[] = Shift<ReducedA>
  , Last extends A[keyof A] = A[Rest['length']]
  > = {
  [K in keyof ReducedA]: Rest['length'] extends 0 ? Unshift<Last, Result> : Merge<A, B, Rest, Unshift<Last, Result>> 
}[0]

type AB = Merge<['a', 'b', 'c'], ['d','e','f']>
// ['a', 'b', 'c', 'd', 'e','f']

Полный код доступен в на игровой площадке .

Оба типа основаны на типах рекурсии, например, для первого типа Append:

  • ReducedT - это массив, который мы будем использовать для извлечения элементов из исходного массива
  • Result - это наш окончательный массив, мы добавляем в него вещи (обратите внимание мы начинаем с [A], поэтому с элемента мы хотим иметь последний
  • Rest - он представляет следующую итерацию ReducedT, поэтому мы сдвигаем один элемент
  • Last - последний элемент из исходного массива
  • Rest['length'] extends 0 ? Unshift<Last, Result> : Append<T, A, Rest, Unshift<Last, Result>> - каждая итерация проверяет, не является ли наш уменьшенный массив не пустым, если мы делаем последний unshift и завершаем рекурсию, если он не пустой, мы снова добавляем с меньшим ReducedT и большим Result при переходе от одного к другому и отмене смещения.
  • [K in keyof ReducedT] здесь только для того, чтобы обмануть систему типа TS, так как без этого TS будет жаловаться на бесконечный вызов ?
...