Variadic Tuple Types
Variadic Tuple Types (вариативные кортежи) — возможность TypeScript 4.0 использовать spread-синтаксис
...Tв позиции типа кортежа, позволяя создавать типы, которые объединяют, разбивают или трансформируют кортежи с сохранением полной типовой информации.
Зачем нужно
До TS 4.0 типизировать функции вроде concat, prepend или curry было невозможно без перегрузок для каждой длины кортежа. Variadic tuple types решают это обобщённо: TypeScript сохраняет типы каждого элемента при объединении кортежей.
Где используется
- Типизация
concat/prepend/appendдля кортежей - Curry и partial application функций
- Compose/pipe с полными типами
- HOF обёртки, сохраняющие параметры исходной функции
Основной контент
Базовый синтаксис spread в кортежах
type Concat<T extends unknown, U extends unknown> = [...T, ...U];
type AB = Concat<[string, number], [boolean, Date]>;
// [string, number, boolean, Date]
type Prepend<T, Arr extends unknown> = [T, ...Arr];
type Append<Arr extends unknown, T> = [...Arr, T];
type P = Prepend<string, [number, boolean]>; // [string, number, boolean]
type A = Append<[string, number], boolean>; // [string, number, boolean]
Извлечение head и tail
type Head<T extends unknown> =
T extends [infer H, ...unknown] ? H : never;
type Tail<T extends unknown> =
T extends [unknown, ...infer Rest] ? Rest : never;
type H = Head<[1, 2, 3]>; // 1
type T = Tail<[1, 2, 3]>; // [2, 3]
type Last<T extends unknown> =
T extends [...unknown, infer L] ? L : never;
type L = Last<[1, 2, 3]>; // 3
Типизация concat-функции
function concat<T extends unknown, U extends unknown>(
a: [...T],
b: [...U]
): [...T, ...U] {
return [...a, ...b];
}
const result = concat([1, "hello"], [true, new Date]);
// [number, string, boolean, Date] — точные типы!
result[0]; // number
result[2]; // boolean
Spread в Parameters и ReturnType
// Типизация curry с variadic tuples
type Curry<Args extends unknown, R> =
Args extends [infer First, ...infer Rest]
? (arg: First) => Curry<Rest, R>
: R;
type CurriedAdd = Curry<[number, number], number>;
// (arg: number) => (arg: number) => number
Labeled tuple elements (TS 4.0+)
// Имена элементов кортежа для лучшей документации
type Range = [start: number, end: number];
type Point3D = [x: number, y: number, z: number];
function createRange(start: number, end: number): Range {
return [start, end];
}
// В IDE при hover показывает: [start: number, end: number]
Практический пример: обёртка с логированием
function withLogging<Args extends unknown, R>(
fn: (...args: Args) => R,
label: string
): (...args: Args) => R {
return (...args: Args): R => {
console.log(`[${label}] args:`, args);
const result = fn(...args);
console.log(`[${label}] result:`, result);
return result;
};
}
function add(a: number, b: number): number { return a + b; }
const loggedAdd = withLogging(add, "add");
loggedAdd(1, 2); // типобезопасно: (a: number, b: number) => number
Частые ошибки
- Использовать
Tвместо[...T]в параметрах функции —[...T]инструктирует TypeScript сохранять кортежные типы, а не расширять до массива. - Spread только в одной позиции — TypeScript разрешает произвольное количество spreads в кортеже, но не два rest-элемента
[...A, ...B, ...C]— только последовательные фиксированные + один rest в конце. - Путать с JavaScript spread — variadic tuple types — это конструкция системы типов, а не рантайм.
- Ожидать вывод без явных
[...T]— без явного[...T]в сигнатуре TypeScript может вывести(string | number)вместо кортежа.
Связанные темы
- Infer -- извлечение типов
- Generic функции
- ReturnType, Parameters, InstanceType
- Conditional types
- _MOC TypeScript