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