Przejdź do treści
webtype.orgwebtype.org

    ↑↓ ruch · ⏎ otwórz · esc zamknij

    Zagadnienie 5 z 12

    Modyfikatory

    Modyfikatory to drobny druk typów mapowanych. Łatwo je napisać i łatwo subtelnie pomylić, bo domyślne zachowanie — ciche kopiowanie tego, co już było — wygląda identycznie jak nierobienie niczego.

    Prześledź typSprawdzone kompilatorem
    WyrażenieDraft
    Rozwija się do{ readonly name?: string }
    WyrażenieReady<Draft>
    Rozwija się do{ name: string }

    Usuń readonly i opcjonalność z każdej właściwości. Tutaj name jest wymagane i zapisywalne.

    Definicja typu
    type Draft = { readonly name?: string }
    type Ready<T> = { -readonly [K in keyof T]-?: T[K] }

    Mapowania homomorficzne dziedziczą modyfikatory

    interface Draft { title?: string; readonly id: number }
    
    // Mapping over bare `keyof T` preserves ? and readonly:
    type Copy<T> = { [K in keyof T]: T[K] }
    type Same = Copy<Draft>   // { title?: string; readonly id: number }
    
    // Mapping over a computed key set does NOT:
    type Flat<T> = { [K in keyof T | never]: T[K] }
    type Bare = Flat<Draft>   // { title: string; id: number }

    Mapowanie jest homomorficzne tylko wtedy, gdy zbiór kluczy to dokładnie keyof T dla parametru T. Wylicz zbiór inaczej — złącz, przefiltruj, przemianuj przez as — a wszystkie modyfikatory domyślnie przepadną.

    To zaskakuje przy pisaniu typu Merge: wynik traci opcjonalność z obu źródeł i nic o tym nie ostrzega.

    -? usuwa też undefined

    type T = { a?: string }        // a?: string | undefined
    type R = { [K in keyof T]-?: T[K] }
    // { a: string }  — not { a: string | undefined }

    Odjęcie modyfikatora opcjonalności robi dwie rzeczy naraz: klucz staje się wymagany, a z typu wartości znika undefined. Tę drugą łatwo przeoczyć — i zwykle właśnie o nią chodziło.

    Najczęstsza błędna odpowiedź

    // Intent: make everything optional.
    type Loose<T> = { [K in keyof T]: T[K] | undefined }
    
    // The key is still required — you must write { a: undefined }
    // rather than omitting it. Optional and "may be undefined"
    // are genuinely different properties.

    Rozszerzenie wartości to nie to samo co uczynienie klucza opcjonalnym, a testy dokładnej równości je rozróżniają. Jeśli chodziło o opcjonalność, ? należy postawić po klauzuli klucza.

    Ćwiczenie

    Usuń readonly z każdej właściwości, a ? zostaw w spokoju.

    Spróbuj

    1
    type Frozen = { readonly a: string; readonly b?: number }
    • Thaw<Frozen>—
      → { a: string; b?: number }

    Do zapamiętania

    Modyfikatory działają na właściwości, nigdy na wartości. A mapowanie, które w ogóle rusza zbiór kluczy, zapomina każdy modyfikator, którego nie kazano mu zachować.

    Ćwiczone w