Przejdź do treści
webtype.orgDwanaście zagadnień · 9/12

Zagadnienie 9 z 12

Typy literałów szablonowych

Typy literałów szablonowych pozwalają kompilatorowi budować i dopasowywać łańcuchy. W połączeniu z `infer` i rekurencją potrafią parsować — tak ścieżka staje się obiektem parametrów, a klucze i18n i nazwy kolumn ORM są sprawdzane w czasie kompilacji.

Budowanie i zamiana na łańcuch

type Greeting = `hello ${string}`
type Event = `on${'Click' | 'Focus'}`   // 'onClick' | 'onFocus'
type AsText = `${42}`                    // '42'

// Interpolating a union multiplies out every combination.
type Cell = `${'a' | 'b'}${1 | 2}`       // 'a1' | 'a2' | 'b1' | 'b2'

Wstawienie unii daje iloczyn kartezjański, który rośnie szybko — TypeScript ogranicza wynik do 100 000 elementów i powyżej zgłasza błąd. Zamiana liczby na łańcuch przez `` `${N}` `` to standardowy sposób przekształcenia problemu arytmetycznego w tekstowy.

Dopasowanie jest zakotwiczone i zachłanne od lewej

type StartsWith<S, P extends string> = S extends `${P}${string}` ? true : false
type EndsWith<S, P extends string> = S extends `${string}${P}` ? true : false
type Contains<S, P extends string> = S extends `${string}${P}${string}` ? true : false

// Greedy from the left: Head binds the SHORTEST prefix that works.
type Split1<S> = S extends `${infer Head}-${infer Tail}` ? [Head, Tail] : never
type X = Split1<'a-b-c'>   // ['a', 'b-c']

Wzorzec musi dopasować cały łańcuch, więc miejsce, gdzie wstawisz `${string}`, jest miejscem dopuszczonego luzu. Luz z przodu znaczy „zawiera”, z tyłu — „zaczyna się od”, a po obu stronach oznacza przypadkowy test podłańcucha.

Cztery wbudowane typy wielkości liter

Uppercase<'abc'>      // 'ABC'
Lowercase<'ABC'>      // 'abc'
Capitalize<'abc'>     // 'Abc'
Uncapitalize<'Abc'>   // 'abc'

Te cztery są zaimplementowane natywnie w kompilatorze i nie mają źródła w TypeScripcie. Sięgaj po nie, zanim napiszesz rekurencję znak po znaku — `CamelCase` i podobne to z nimi kilka linii, a bez nich koszmar.

Najczęstsza błędna odpowiedź

type Replace<S extends string, From extends string, To extends string> =
  S extends `${infer H}${From}${infer T}` ? `${H}${To}${T}` : S

Replace<'abc', '', 'x'>   // 'xabc' — not 'abc'

Pusty łańcuch mieści się między dowolnymi dwoma znakami, więc pusta igła dopasowuje się natychmiast z pustym `H`. To `null` typów literałów szablonowych: pasuje wszędzie i po cichu psuje wzorce zakładające, że dopasowanie oznacza znalezienie czegoś.

Do zapamiętania

Wzorce są zakotwiczone po obu stronach i zachłanne od lewej. Miejsce dopuszczonego luzu decyduje, o co naprawdę pytasz, a pusty łańcuch zawsze jest przypadkiem brzegowym.

Ćwiczone w