TypeScript: Все перестановки в union
Понадобилось по работе собрать из union
список туплов со всеми возможными перестановками. То есть, чтобы из "a" | "b"
получалось ["a"] | ["b"] | ["a", "b"] | ["b", "a"]
. Написал в лоб решение на 15 строк и подумал, что это как-то дофига. Начал ковырять плейграунд и пытаться соптимизировать.
Дооптимизировал до одной строки, и полчаса сам вкуривал, как это работает:
// Все перестановки в union
type Permutations<T, U = T> = T extends U ? [ T ] | [ T, ...Permutations<Exclude<U, T>> ] : never;
type Test = Permutations<'a' | 'b'>; // ["a"] | ["b"] | ["a", "b"] | ["b", "a"]
Всё время я про эту фичу забываю. Запишу-ка тут, чтобы потом было где подсмотреть.
Магия происходит из-за того, что вот такая конструкция T extends U ? [ T ] : never
, раскладывается на юнион из кортежей. То есть, в качестве T
, в результате тернарного extends
, будет по очереди подставлено каждое значение из пересечения T
и U
:
type Test<T, U = T> = T extends U ? [ T ] : never;
type Test2 = Test<'a' | 'b'>; // ["a"] | ["b"]
Ну, а дальше уже понятно.