Template Literal Types
Sixian Li
Syntax
JavaScript template literal string
const a = `string text ${expression} string text`
TypeScript template literal types
type World = "world";
type Greeting = `hello ${World}`;
= "hello world"
Union
When a union is used in the interpolated position, the result is distributed.
"You fill the gap with each of the value."
type World = "world" | "tomorrow";
type Greeting = `hello ${World}`;
= "hello world" | "hello tomorrow"
TemplateLiteral<A | B | C> =
TemplateLiteral<A> | TemplateLiteral<B> | TemplateLiteral<C>
Color/Animal | goose | hamster |
---|---|---|
black | (black, goose) | (black, hamster) |
white | (white, goose) | (white, hamster) |
purple | (purple, goose) | (purple, hamster) |
type Colors = "black" | "white" | "purple"
type Animals = "goose" | "hamster"
type ColoredAnimals = `${Colors} ${Animals}`
= "black goose" | "black hamster" | "white goose" | "white hamster" | "purple goose" | "purple hamster"
Multiple interpolated positions
Destructuring
`infer`
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type EverythingBeforeWorld<T> = T extends `${infer H}World` ? H : never;
type T1 = EverythingBeforeWorld<"Hello World">;
= "Hello "
Inferred type variables may be referenced in the true branch of the conditional type
Match on the structure of the string
Smart querySelector
Current querySelector
// lib.dom.d.ts
/** Returns the first element that is a descendant of node that matches selectors. */
querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;
querySelector<K extends keyof SVGElementTagNameMap>(selectors: K): SVGElementTagNameMap[K] | null;
querySelector<E extends Element = Element>(selectors: string): E | null;
const a = document.querySelector('div') // HTMLDivElement
const b = document.querySelector('div > button') // Element
const c = document.querySelector(' ') // Element, but run-time error
Smart querySelector
document.querySelector('div#app') // ==> HTMLDivElement
document.querySelector('div#app > form#login') // ==> HTMLFormElement
document.querySelectorAll('span.badge') // ==> NodeListOf<HTMLSpanElement>
anElement.querySelector('button#submit') // ==> HTMLButtonElement
Smart querySelector
type QuerySelector<TSelector extends string> = IsInvalid<TSelector> extends true
? never
: TagNameToElement<HandleSimpleSelector<GetLastSimpleSelector<TSelector>>>;
Bonus:
Tips for writing generics
Type Parameters Should Appear Twice
function identity<T>(arg: T): T {
// (dec) 1 2
return arg;
}
// K only appeared once
function printProperty<T, K extends keyof T>(obj: T, key: K) {
console.log(obj[key]);
}
function printProperty<T>(obj: T, key: keyof T) {
console.log(obj[key]);
}
References
- https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html
- https://github.com/type-challenges/type-challenges
- https://devblogs.microsoft.com/typescript/type-treat-2021-day-4/
- https://github.com/ghoullier/awesome-template-literal-types
- https://github.com/g-plane/type-gymnastics
- https://effectivetypescript.com/2020/08/12/generics-golden-rule
THANK YOU
Copy of Template Literal Types
By Sixian Li
Copy of Template Literal Types
- 107