Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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

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

where ModiferKeyCombinations expected to be:

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'
like image 286
aztack Avatar asked Jul 05 '21 07:07

aztack


People also ask

Can you concatenate template literals?

With template literals, you can avoid the concatenation operator — and improve the readability of your code — by using placeholders of the form ${expression} to perform substitutions for embedded expressions: const a = 5; const b = 10; console.

What is a template literal in TypeScript?

Template literal types build on string literal types, and have the ability to expand into many strings via unions. They have the same syntax as template literal strings in JavaScript, but are used in type positions.

Are template literals faster?

popular templaters like mustache/underscore/handlebars are dozens to hundreds of times slower than concatenation is or template literals will be. Theoretical speaking (unless the JS is compiled), template literals would be slower since the 'string' needs to be parsed regardless of placeholder existence.

What is the use of template literals?

Template literals provide an easy way to interpolate variables and expressions into strings. The method is called string interpolation.


1 Answers

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

like image 182
jcalz Avatar answered Sep 25 '22 17:09

jcalz