When working with string literal types in TypeScript, the use of the infer keyword can lead to some intriguing behaviors. Let's delve into an example to explore its nuances:
type Test<T> = T extends `${infer A}${infer B}${infer C}`
? `${A}-${B}-${C}`
: never;
type Test1 = Test<"a">; // never
type Test2 = Test<"ab">; // a-b-
type Test3 = Test<"abcd">; // a-b-cd
Observations:
- The first result is
neverbecause the string literal type is only one character long, and our type requires at least three characters. - The second result,
a-b-, may seem unexpected. Theinferkeyword is greedy, attempting to match as many characters as possible. In this case, it matchesatoA,btoB, and the remaining empty string toC. As we can only have one empty string in the template literal type, the result isa-b-. This behavior becomes clearer with a four-character example:
type Test4Char<T> = T extends `${infer A}${infer B}${infer C}${infer D}`
? `${A}-${B}-${C}-${D}`
: never;
type Test1 = Test4Char<"a">; // never (B, C, and D are empty strings, and only one is allowed)
type Test2 = Test4Char<"ab">; // never (C & D are empty strings, and only one is allowed)
type Test3 = Test4Char<"abc">; // a-b-c- (D is empty string)
type Test4 = Test4Char<"abcd">; // a-b-c-d
- The third result,
a-b-cd, demonstrates thatCmatchescd, storing the remaining string inC.
With this understanding, let's leverage this knowledge to create some intriguing types:
// Get the last character of a string
type LastChar<T extends string> = T extends `${infer A}${infer Last}`
? Last extends ""
? A
: LastChar<Last>
: never;
type TestL1 = LastChar<"abcd">; // 'd'
// Reverse a string
type Reverse<T extends string> = T extends `${infer A}${infer B}`
? `${Reverse<B>}${A}`
: T;
type TestR1 = Reverse<"reverse">; // esrever
// Convert camel case to kebab case
type CamelToKebab<T> = T extends `${infer A}${infer B}`
? `${A extends Capitalize<A> ? `-${Lowercase<A>}` : A}${CamelToKebab<B>}`
: T;
type TestC1 = CamelToKebab<"fooBarBaz">; // foo-bar-baz
type TestC2 = CamelToKebab<"fooBar">; // foo-bar
type TestC3 = CamelToKebab<"foo">; // foo
That's all for now. Happy typing!
Top comments (0)