Concepto 5 de 12
Modificadores
Los modificadores son la letra pequeña de los tipos mapeados. Son fáciles de escribir y fáciles de equivocar sutilmente, porque el comportamiento por defecto — copiar en silencio lo que ya había — se ve idéntico a no hacer nada.
Los mapeos homomórficos heredan modificadores
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 }
Un mapeo es homomórfico solo cuando el conjunto de claves es exactamente `keyof T` para un parámetro `T`. Calcula el conjunto de otra forma — únelo, fíltralo, renómbralo con `as` — y todos los modificadores se pierden por defecto.
Esto pilla a la gente al escribir un tipo `Merge`: el resultado pierde la opcionalidad de ambas fuentes, y nada te avisa.
`-?` también quita undefined
type T = { a?: string } // a?: string | undefined type R = { [K in keyof T]-?: T[K] } // { a: string } — not { a: string | undefined }
Restar el modificador opcional hace dos cosas a la vez: la clave pasa a ser obligatoria y se elimina `undefined` del tipo del valor. Esa segunda parte es fácil de olvidar y suele ser justo lo que querías.
El error más común
// 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.
Ensanchar el valor no es lo mismo que hacer la clave opcional, y las comprobaciones de igualdad exacta los distinguen. Si querías opcional, el `?` va tras la cláusula de clave.
Para recordar
Los modificadores actúan sobre la propiedad, nunca sobre el valor. Y un mapeo que toque el conjunto de claves olvida todo modificador que no se le dijo que conservara.