こんな感じの型です
type Fuyu = Permutation<'あんたは' | 'ここで' | 'ふゆと' | '死ぬのよ'>; // ['あんたは', 'ここで', 'ふゆと', '死ぬのよ'] | ['あんたは', 'ここで', '死ぬのよ', 'ふゆと'] | ['あんたは', 'ふゆと', 'ここで', '死ぬのよ'] | ...
※本記事ではTypeScript beta版の機能を使用しています
作り方
けっこう愚直に書いてます。そこまで説明することもないですね。
type Permutation<T, U = T> = U extends string ? Exclude<T, U> extends never ? [U] : [U, ...Permutation<Exclude<T, U>>] : [U];
ポイントはTとUの2変数を使っている点です。こうしないと、conditional types でバラされたあとに元々の型がわからなくなってしまい、Exclude<T, U>
の部分が実現できません。
気をつけるべきはベータバージョンでしか動かないことです(2020年10月現在)。頑張れば下位バージョンでもできそうですが、まだ思いついていないです。
実例
type Permutation<T, U = T> = U extends string ? Exclude<T, U> extends never ? [U] : [U, ...Permutation<Exclude<T, U>>] : [U]; type ArrayToString<T extends string[]> = T extends [infer head, ...infer tail] ? head extends string ? tail extends string[] ? `${head}${ArrayToString<tail>}` : '' : '' : ''; type Fuyu = ArrayToString<Permutation<'あんたは' | 'ここで' | 'ふゆと' | '死ぬのよ'>>; const str: Fuyu[] = ['あんたはここでふゆと死ぬのよ', 'あんたはふゆとここで死ぬのよ', 'ここであんたはふゆと死ぬのよ']; const normalize = (str: Fuyu) => 'あんたはここでふゆと死ぬのよ' as const;
おまけ: 不要なものを除外する
上記の例では「死ぬのよここでふゆとあんたは」のようなものまで含んでしまっています。不要なものを除外するときはExcludeを使います。
type Fuyu = Exclude< ArrayToString< Permutation<'あんたは' | 'ここで' | 'ふゆと' | '死ぬのよ'> >, '死ぬのよここでふゆとあんたは'>;