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

    ↑↓ ruch · ⏎ otwórz · esc zamknij

    Zagadnienie 4 z 12

    Typy mapowane

    Typ mapowany to pętla po unii kluczy, która buduje obiekt. `Partial`, `Required`, `Readonly` i `Pick` to po jednej linii tego zapisu — a gdy kształt stanie się znajomy, napiszesz każdy z nich z pamięci.

    Kształt

    type MyPartial<T> = { [K in keyof T]?: T[K] }
    //                     ^^^^^^^^^^^^^^  ^  ^^^^
    //                     key set         |  value type
    //                                     modifier

    Czytaj to tak: dla każdego `K` w tym zbiorze kluczy stwórz właściwość o nazwie `K` i typie z tego wyrażenia. Zbiór kluczy to dowolna unia `string`, `number` lub `symbol` — zwykle `keyof T`, czasem parametr, sporadycznie coś wyliczonego.

    Modyfikatory dodają i odejmują

    type Optional<T> = { [K in keyof T]+?: T[K] }   // add ?
    type Required<T> = { [K in keyof T]-?: T[K] }   // remove ?
    type Frozen<T> = { readonly [K in keyof T]: T[K] }
    type Thawed<T> = { -readonly [K in keyof T]: T[K] }

    Mapowanie po gołym `keyof T` jest *homomorficzne*, co znaczy, że istniejące modyfikatory `?` i `readonly` są kopiowane automatycznie. Dlatego mapowanie tożsamościowe niczego nie zmienia i dlatego usunięcie modyfikatora wymaga jawnego minusa.

    Zmiana nazw kluczy przez `as`

    type Getters<T> = {
      [K in keyof T as `get${Capitalize<K & string>}`]: () => T[K]
    }
    
    // A key renamed to never disappears — this is how you filter.
    type OnlyStrings<T> = {
      [K in keyof T as T[K] extends string ? K : never]: T[K]
    }

    Klauzula `as` przepisuje każdy klucz w trakcie mapowania. Klucz przepisany na `never` nie może istnieć, więc właściwość znika — i to jedyny sposób usunięcia właściwości z typu obiektowego.

    Najczęstsza błędna odpowiedź

    // Intent: keep only the string-valued properties.
    type OnlyStrings<T> = {
      [K in keyof T]: T[K] extends string ? T[K] : never
    }
    // Every key survives; the rejected ones just hold never.
    // { name: string; age: never }

    Filtrowanie po stronie wartości nie usuwa klucza, tylko go opróżnia. Filtruje się w klauzuli klucza przez `as ... : never`, a pomylenie tych miejsc daje typ, który wygląda na przefiltrowany i niczego nie egzekwuje.

    Ćwiczenie

    Zamień każdą właściwość `T` na `string`, zachowując te same klucze.

    Spróbuj

    1
    type User = { id: number; admin: boolean }
    • Stringify<User>
      { id: string; admin: string }

    Do zapamiętania

    `{ [K in Klucze]: Wartość }` to pętla. Modyfikatory doczepiają się do właściwości, `as` przepisuje klucz, a `never` jako klucz sprawia, że rzeczy znikają.

    Ćwiczone w