Система типов TypeScript на самом деле не предназначена для того, чтобы позволить вам сделать это. Наиболее очевидной реализацией чего-то вроде Flatten
будет рекурсивный условный тип; в настоящее время не поддерживается (см. Microsoft / TypeScript # 26980) . Вы можете сделать это, но нет никакой гарантии, что он будет работать в будущих версиях TypeScript. И даже если вы получите рабочую версию, ее действительно легко сделать так, чтобы она взорвала компилятор TypeScript, вызывая исключительно длительное время компиляции и даже зависание и сбой компилятора. Первая версия Flatten
, которую я написал в качестве теста, имела эту проблему, постоянно занимаясь обработкой даже выходного кортежа длиной 7, и время от времени сообщая об ошибках глубины создания типов.
Я думаю, что канонической проблемой GitHub для этой функции может быть microsoft / TypeScript # 5453 , предложение о поддержке variadi c (произвольная длина) виды (в основном "типы типов") . В настоящее время единственный официально поддерживаемый способ манипулирования кортежами в варианте c состоит в добавлении фиксированного числа типов в начало с использованием кортежей в позициях покоя / спреда .
Таким образом, «официальный» ответ - это что-то вроде «вы не можете или не должны этого делать», но это не мешает людям делать это. Есть даже библиотека под названием ts-toolbelt
, которая делает все виды забавных рекурсивных вещей под прикрытием, чтобы получить больше произвольных манипуляций с кортежами. Я думаю, что автор этой библиотеки действительно постарался сделать так, чтобы производительность компилятора не пострадала, поэтому, если бы я собирался что-то реально использовать, я бы, вероятно, использовал эту библиотеку, а не писал сам. Один из инструментов на этом ремне называется Flatten<L>
, который, кажется, делает то, что вы хотите. Но даже эта библиотека все еще официально не поддерживается .
Тем не менее я не удержался от написания своей собственной версии Flatten
, чтобы дать вам некоторое представление о том, насколько она волосатая. Кажется, он работает достаточно хорошо. Я ограничил его конкатенацией только до 7 кортежей, а общая длина выровненного вывода не может превышать около 30 элементов. Он использует итеративные и рекурсивные условные типы, последний из которых не поддерживается. Особенно умный человек может придумать способ сделать это совершенно итеративно, но я либо не тот человек, либо мне понадобится слишком много времени, чтобы стать этим человеком. Хорошо, достаточно преамбулы, вот она:
/*
codegen
var N = 30;
var range = n => (new Array(n)).fill(0).map((_,i)=>i);
var s = [];
s.push("type Add = ["+range(N).map(i => "["+range(N-i).map(j => i+j+"").join(",")+"]").join(",")+"];")
s.push("type Sub = ["+range(N).map(i => "["+range(i+1).map(j => i-j+"").join(",")+"]").join(",")+"];")
s.push("type Tup = ["+range(N).map(i => "["+range(i).map(_=>"0").join(",")+"]").join(",")+"];")
console.log(s.join("\n"))
*/
type Add = [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [20, 21, 22, 23, 24, 25, 26, 27, 28, 29], [21, 22, 23, 24, 25, 26, 27, 28, 29], [22, 23, 24, 25, 26, 27, 28, 29], [23, 24, 25, 26, 27, 28, 29], [24, 25, 26, 27, 28, 29], [25, 26, 27, 28, 29], [26, 27, 28, 29], [27, 28, 29], [28, 29], [29]];
type Sub = [[0], [1, 0], [2, 1, 0], [3, 2, 1, 0], [4, 3, 2, 1, 0], [5, 4, 3, 2, 1, 0], [6, 5, 4, 3, 2, 1, 0], [7, 6, 5, 4, 3, 2, 1, 0], [8, 7, 6, 5, 4, 3, 2, 1, 0], [9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0], [29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]];
type Tup = [[], [0], [0, 0], [0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]];
type Arr = readonly any[];
type Tail<T extends Arr> =
((...x: T) => void) extends ((h: infer A, ...t: infer R) => void) ? R : never
type Concat<T extends Arr, U extends Arr> = Tup[Add[T["length"]][U["length"]]] extends infer A ? {
[I in keyof A]: I extends keyof T ? T[I] : U[Sub[Extract<I, keyof Sub>][T["length"]]]
} : never
type Tuplize<T> = { [K in keyof T]: T[K] extends any[] ? T[K] : [T[K]] }
type Flatten<T extends readonly any[], N extends number = 7> =
N extends 0 ? [] :
Tuplize<T> extends infer U ? U extends Arr ?
{ 0: [], 1: Concat<U[0], Extract<Flatten<Tail<U>, Extract<Sub[N][1], number>>, Arr>> }[
U extends [] ? 0 : 1] : never : never;
Первые три строки генерируются небольшим JS сценарием, чтобы создать набор фиксированных кортежей, представляющих операции сложения и вычитания чисел, и также получить «пустой» кортеж заданной длины. Таким образом, Add[3][4]
должно быть 7
, Sub[7][3]
должно быть 4
, а Tup[3]
должно быть [0,0,0]
.
Оттуда я определяю Tail<T>
, который принимает кортеж типа [1,2,3,4]
и удаляет первый элемент для получения [2,3,4]
, и Concat<T, U>
, который принимает два кортежа и объединяет их (например, Concat<[1,2],[3,4]>
должно быть [1,2,3,4]
). Определение Concat
здесь чисто итеративное, поэтому оно еще не является незаконным.
Затем я делаю Tuplize<T>
, что просто гарантирует, что каждый элемент кортежа T
сам является массивом. Таким образом, ваш [A, B, [C, D], [A]]
станет [[A],[B],[C,D],[A]]
. Это удаляет странные граничные условия, которые вы получаете при выравнивании.
Наконец я пишу недопустимый и рекурсивный Flatten<T>
. Я пытался установить там некоторое ограничение рекурсии; это будет работать только для длины до 7 или около того. Если вы попытаетесь увеличить его, просто изменив 7
на 25
, вы можете получить ошибки от компилятора. В любом случае, базовый c подход заключается в выполнении операции reduce()
на Tuplize<T>
: просто Concat
первый элемент Tuplize<T>
в Flatten
-данной версии Tail
Tuplized<T>
.
Давайте рассмотрим пример:
type InputTuple = [A, B, [C, D], [A, B, D], [A, B, C, D, B], A];
type FlattenedTuple = Flatten<InputTuple>;
// type FlattenedTuple = [A, B, C, D, A, B, D, A, B, C, D, B, A]
Хорошо выглядит.
Здесь есть все предостережения; он выравнивает только один уровень (что вы и просили). Это, вероятно, не распространяется по профсоюзам. Он может работать не так, как вы хотите, с readonly
или дополнительными кортежами или массивами. Это определенно не будет работать должным образом с кортежами, которые являются слишком длинными или произвольной длины. Он может не работать должным образом, если луна полная или Юпитер выравнивается с Марсом и т. Д. c.
Смысл приведенного выше кода - , а не , чтобы вы могли добавить его в свою производственную систему. Пожалуйста, не делай этого. Просто повеселиться с системой типов и показать, что это открытый вопрос.
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код