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

You can make the compiler calculate such permutations, although since the number of permutations grows exponentially with the number of elements, you should be careful using it. Here’s how I’d proceed:

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

and then the type you want is to pass Permutations the union of the strings you want to permute:

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

You can verify they are the same type by declaring a var multiple times with both that type and the manually created one from your question, and seeing that the compiler is happy with it:

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

The way Permutations<T> works: first, I have to give it the full union twice; once as the T parameter, and once as the U parameter. That’s because we need to pull apart this union into its pieces while also maintaining it so we can remove one element with 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

If you call Permutations<T> when T is never (meaning zero strings), you get never out.

If you call Permutations<T> when T is one string like "oneString", then you use Permutations<never> as part of the answer: "oneString" | `oneString ${never}`… the latter of which becomes just never itself according to the rules for template literal strings. So just "oneString".

If you call Permutations<T> when T is a union of two strings, like "a" | "b", then you use Permutations<"a"> and Permutations<"b"> as part of the answer: "a" | `a ${Permutations<"b">}` | "b" | `b ${Permutations<"a">}`, which becomes "a" | "a b" | "b" | "b a".

…and so forth; I’ll stop there.

Playground link to code

Leave a Comment