Przejdź do treści
webtype.orgwebtype.org

    ↑↓ ruch · ⏎ otwórz · esc zamknij

    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.

    Prześledź typSprawdzone kompilatorem
    WyrażenieEventName<"name">
    Rozwija się do"nameChanged"
    WyrażenieEventName<"name" | "age">
    Rozwija się do"nameChanged" | "ageChanged"

    Unia w szablonie tworzy literał łańcucha dla każdego elementu. Dzieje się to w systemie typów, bez uruchamiania JavaScriptu.

    Definicja typu
    type EventName<T extends string> = `${T}Changed`

    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ś.

    Ćwiczenie

    Zbuduj nazwę obsługi zdarzenia: click staje się onClick.

    Spróbuj

    2
    • Handler<'click'>—
      → 'onClick'
    • Handler<'focus' | 'blur'>—
      → 'onFocus' | 'onBlur'

    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