El tipo Record de TypeScript, explicado con tus propias claves
Tipo Record de TypeScript
Record<K, V> en TypeScript: objetos de búsqueda tipados | We.Inc
Crea un tipo Record<K, V> con tus propias claves y mira el objeto que describe. Aprende cuándo Record supera a una firma de índice o a un Map, y qué errores detecta.
Record<K, V> es un tipo de utilidad integrado para un objeto cuyas claves vienen de K y cuyos valores son todos de tipo V. Si le das una unión de literales de cadena, el compilador exige que estén todas las claves de la lista. Si le das string a secas, se comporta como un diccionario abierto, igual que una firma de índice.
Record de un vistazo
Escribes
Significa
¿Se permite que falte una clave?
Record<"a" | "b", number>
{ a: number; b: number }
No, ambas claves son obligatorias
Record<string, number>
{ [key: string]: number }
Sí, cualquier clave de tipo string
Partial<Record<"a" | "b", number>>
{ a?: number; b?: number }
Sí, cada clave es opcional
Record<number, string>
{ [key: number]: string }
Sí, cualquier clave numérica
Escribe las claves que quieres y elige un tipo de valor. El generador escribe el tipo Record, el tipo de objeto simple al que se expande y un objeto inicial con una línea por clave, para que veas exactamente qué te exigirá el compilador antes de pegarlo en tu código.
Escribe tus claves separadas por comas, por ejemplo home, about, contact.
Elige el tipo de valor que debe tener cada clave.
Marca opcional si algunas claves pueden faltar, lo que envuelve el tipo en Partial.
Copia el tipo y el objeto inicial en tu archivo y rellena los valores.
Obtén la unión de claves de datos que ya tienes: keyof typeof someObject o (typeof list)[number] tras un array as const mantiene el Record sincronizado con la fuente.
Recurre a Record<string, T> solo cuando las claves llegan de verdad en tiempo de ejecución. Una unión cerrada detecta erratas en los nombres de propiedades que un diccionario abierto deja pasar.
Activa noUncheckedIndexedAccess si lees de Record<string, T> con una clave calculada. Así las lecturas devuelven T | undefined, que es lo que ocurre en tiempo de ejecución.
Evita Record<string, any>. Record<string, unknown> sigue aceptando cualquier objeto, pero obliga a comprobar un valor antes de usarlo.
Preguntas frecuentes
¿Record es lo mismo que una firma de índice?
Con una clave amplia como string, sí: Record<string, number> y { [key: string]: number } describen la misma forma. La diferencia aparece con claves literales. Una firma de índice no puede exigir que existan nombres concretos, mientras que Record<"a" | "b", number> exige tanto a como b.
¿Cuándo debo usar un Map en lugar de un Record?
Usa un Map cuando las claves se añaden y se eliminan a menudo, cuando las claves no son cadenas o cuando necesitas el número de entradas mediante size. Usa un Record para un conjunto fijo de nombres, para objetos de configuración y para cualquier cosa que pases por JSON.stringify.
¿Por qué TypeScript dice que falta una propiedad en mi Record?
Porque el tipo de clave es una unión y el objeto deja fuera uno de sus miembros. tsc informa, por ejemplo, Property 'about' is missing in type '{ home: string; }'. Añade la clave, o envuelve el tipo en Partial si es válido que falte.
¿Record existe en tiempo de ejecución?
No. Es solo un tipo y desaparece al compilar el código. El objeto que creas es un objeto JavaScript normal, así que el compilador no puede impedir que la respuesta de un servidor tenga claves de más o de menos.
¿Qué versión de TypeScript añadió Record?
Record llegó en TypeScript 2.1, junto con Partial, Readonly y Pick, así que cualquier compilador actual lo tiene.
Cómo está construido Record
Record es un mapped type. Recorre cada miembro del tipo de clave y le da a cada uno el mismo tipo de valor, por eso Record<"a" | "b", number> se expande a un tipo de objeto con dos propiedades number obligatorias.
Como es solo un tipo, puedes pasarle cualquier cosa que pueda ser una clave de propiedad: literales de cadena, number, string, symbol o una unión de ellos. Un tipo de clave construido a partir de otro tipo, como keyof User, se actualiza solo cuando cambia User.
Claves cerradas frente a claves abiertas
El valor de Record está en la forma cerrada. Una tabla de búsqueda de nombres de rutas, etiquetas de estado o colores de tema tipada con una unión de literales rompe la compilación el día que alguien añade un estado nuevo sin etiqueta, que es justo cuando quieres enterarte.
La forma abierta, Record<string, T>, describe un diccionario. No dice nada sobre qué claves existen, así que leer una clave da T aunque la clave no esté. Combínala con noUncheckedIndexedAccess o con un Map cuando ese hueco importe.
Record, firma de índice o Map
Elige Record con claves literales para búsquedas fijas, Record<string, T> o una firma de índice para diccionarios con forma de JSON, y Map para colecciones que cambian mientras el programa se ejecuta. La guía de Map en TypeScript de este sitio trata a fondo este último caso.
Los patrones que más usarás
Claves a partir de un array as const
El array es la única fuente de verdad. Añádele un estado y el compilador señalará la tabla de etiquetas hasta que añadas también la etiqueta.
Un indicador por cada campo de otro tipo
keyof convierte un tipo existente en la unión de claves, así que el objeto de campos tocados de un formulario siempre lista los mismos campos que el formulario.
Un diccionario abierto, leído de forma segura
Con noUncheckedIndexedAccess activado, una lectura con clave calculada es number | undefined, así que el valor alternativo es obligatorio en lugar de olvidarse.
Generador de tipos Record
Claves, separadas por comas
Tipo de valor
Las claves son opcionales (envolver en Partial)
El tipo
A qué se expande
Un objeto inicial
Escribe al menos una clave. Las claves deben empezar por una letra, _ o $.