Выровнять тип вложенного кортежа и сохранить порядок - PullRequest
1 голос
/ 21 января 2020

Я пытаюсь выполнить sh что-то вроде этого:

type Type = object;
type TypeTuple = readonly Type[];

function flattenTuples<T extends readonly (Type | TypeTuple)[], R = Flatten<T>>(...tuples: T): R {
  // flatten tuple and return with correct ordering
  // example: flattenTuples(A, B, [C, D], [A]) => [A, B, C, D, A]
}

Где функция flattenTuples сгладит каждый кортеж в предоставленном параметре, а реализация типа Flatten<T> сделает то же самое и вернет например, кортеж "as const" массив и сохранить порядок кортежей параметров. Мне нужно только 1 уровень выравнивания.

Пример еще раз (A, B et c - все разные конструкторы классов):

const flat = flattenTuples(A, B, [C, D], [A]);
// this would make the variable flat's type:
// [A, B, C, D, A]

Я попробовал ответить на аналогичный вопрос но его решение типа Flatten не сработало. С примером выше он производит тип [A, B, C | D, A]

1 Ответ

2 голосов
/ 21 января 2020

Система типов 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.

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


Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

...