Saltar al contenido
webtype.orgwebtype.org

    ↑↓ mover · ⏎ abrir · esc cerrar

    Concepto 4 de 12

    Tipos mapeados

    Un tipo mapeado es un bucle sobre una unión de claves que construye un objeto. Partial, Required, Readonly y Pick son una línea de esto cada uno, y cuando la forma te resulte familiar podrás escribir cualquiera de memoria.

    Sigue el tipoVerificado por el compilador
    ExpresiónFlags<"read">
    Se resuelve a{ read: boolean }
    ExpresiónFlags<"read" | "write">
    Se resuelve a{ read: boolean; write: boolean }

    Cada miembro de la unión de claves se convierte en una propiedad. La regla del valor se aplica a cada clave.

    Definición del tipo
    type Flags<K extends string> = { [P in K]: boolean }

    La forma

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

    Léelo así: para cada K de este conjunto de claves, produce una propiedad llamada K cuyo tipo es esa expresión. El conjunto de claves es cualquier unión de string, number o symbol — normalmente keyof T, a veces un parámetro, ocasionalmente algo calculado.

    Los modificadores suman y restan

    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] }

    Mapear sobre un keyof T desnudo es *homomórfico*, lo que significa que los modificadores ? y readonly existentes se copian automáticamente. Por eso un mapeo identidad no cambia nada, y por eso quitar un modificador exige un menos explícito.

    Renombrar claves con 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]
    }

    La cláusula as reescribe cada clave mientras corre el mapeo. Una clave reescrita a never no puede existir, así que la propiedad se elimina — y es la única forma de quitar una propiedad de un tipo objeto.

    El error más común

    // 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 }

    Filtrar del lado del valor no puede eliminar una clave, solo vaciarla. El filtrado ocurre en la cláusula de clave con as ... : never, y confundir ambos produce un tipo que parece filtrado y no impone nada.

    Ejercicio

    Haz que cada propiedad de T sea string, conservando las mismas claves.

    Pruébalo

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

    Para recordar

    { [K in Claves]: Valor } es el bucle. Los modificadores se aplican a la propiedad, as reescribe la clave, y never como clave es cómo desaparecen las cosas.

    Se practica en