Saltar al contenido
webtype.orgwebtype.orgDoce conceptos · 4/12

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

    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