Przejdź do treści
webtype.orgwebtype.org

    ↑↓ 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.

    Prześledź typSprawdzone kompilatorem
    WyrażenieFlags<"read">
    Rozwija się do{ read: boolean }
    WyrażenieFlags<"read" | "write">
    Rozwija się do{ read: boolean; write: boolean }

    Każdy element unii kluczy staje się właściwością. Reguła wartości stosuje się do każdego klucza.

    Definicja typu
    type Flags<K extends string> = { [P in K]: boolean }

    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