是否可以在 TypeScript 中生成带有模板文字的字符串文字组合?

Is it possible to generate string literal combinations with template literal in TypeScript?

是否可以在 TypeScript 中生成字符串文字与模板文字组合的排列?

type MetaKey = 'meta';
type CtrlKey = 'ctrl';
type ShiftKey = 'shift';
type AltKey = 'alt';
type ModiferKeyCombinations = ???

其中 ModiferKeyCombinations 预计为:

type ModiferKeyCombinations = 
  | 'meta'
  | 'ctrl'
  | 'shift'
  | 'alt'
  | 'meta ctrl'
  | 'meta shift'
  | 'meta alt'
  | 'ctrl meta'
  | 'ctrl shift'
  | 'ctrl alt'
  | 'shift meta'
  | 'shift ctrl'
  | 'shift alt'
  | 'alt meta'
  | 'alt ctrl'
  | 'alt shift'
  | 'meta ctrl shift'
  | 'meta ctrl alt'
  | 'meta shift ctrl'
  | 'meta shift alt'
  | 'meta alt ctrl'
  | 'meta alt shift'
  | 'ctrl meta shift'
  | 'ctrl meta alt'
  | 'ctrl shift meta'
  | 'ctrl shift alt'
  | 'ctrl alt meta'
  | 'ctrl alt shift'
  | 'shift meta ctrl'
  | 'shift meta alt'
  | 'shift ctrl meta'
  | 'shift ctrl alt'
  | 'shift alt meta'
  | 'shift alt ctrl'
  | 'alt meta ctrl'
  | 'alt meta shift'
  | 'alt ctrl meta'
  | 'alt ctrl shift'
  | 'alt shift meta'
  | 'alt shift ctrl'
  | 'meta ctrl shift alt'
  | 'meta ctrl alt shift'
  | 'meta shift ctrl alt'
  | 'meta shift alt ctrl'
  | 'meta alt ctrl shift'
  | 'meta alt shift ctrl'
  | 'ctrl meta shift alt'
  | 'ctrl meta alt shift'
  | 'ctrl shift meta alt'
  | 'ctrl shift alt meta'
  | 'ctrl alt meta shift'
  | 'ctrl alt shift meta'
  | 'shift meta ctrl alt'
  | 'shift meta alt ctrl'
  | 'shift ctrl meta alt'
  | 'shift ctrl alt meta'
  | 'shift alt meta ctrl'
  | 'shift alt ctrl meta'
  | 'alt meta ctrl shift'
  | 'alt meta shift ctrl'
  | 'alt ctrl meta shift'
  | 'alt ctrl shift meta'
  | 'alt shift meta ctrl'
  | 'alt shift ctrl meta'

你可以让编译器计算这样的排列,虽然由于排列的数量随着元素的数量呈指数增长,你应该小心使用它。以下是我将如何进行:

type Permutations<T extends string, U extends string = T> =
    T extends any ? (T | `${T} ${Permutations<Exclude<U, T>>}`) : never;

然后你想要的类型是传递 Permutations 你想要置换的字符串的 union:

type ModiferKeyCombinations = Permutations<MetaKey | CtrlKey | ShiftKey | AltKey>;

您可以通过 var 多次声明该类型和您的问题中手动创建的类型来验证它们是同一类型,并查看编译器是否满意:

var x: ModiferKeyCombinations;
var x: ManualModiferKeyCombinations; // no compiler error

Permutations<T>的工作方式:首先,我必须给它两次完整的并集;一次作为 T 参数,一次作为 U 参数。那是因为我们需要将这个并集分解成多个部分,同时还要维护它,这样我们就可以用 the Exclude utility type. The idea is to take each piece T of the full union U, and then return that piece alone as well as concatenating Permutations<Exclude<U, T>> onto the end using template literal string types

删除一个元素

如果在 Tnever(表示零个字符串)时调用 Permutations<T>,则会得到 never

如果当 T 是像 "oneString" 这样的一个字符串时调用 Permutations<T>,那么您使用 Permutations<never> 作为答案的一部分:"oneString" | `oneString ${never}`.. . 根据模板文字字符串的规则,后者变成 never 本身。所以只是 "oneString".

如果在 T 是两个字符串的联合时调用 Permutations<T>,例如 "a" | "b",那么您使用 Permutations<"a">Permutations<"b"> 作为答案:"a" | `a ${Permutations<"b">}` | "b" | `b ${Permutations<"a">}`,变成 "a" | "a b" | "b" | "b a".

...等等;我就到此为止了。

Playground link to code