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

THANK YOU

Made with Slides.com