O tipo Record do TypeScript, explicado com as suas próprias chaves
Tipo Record do TypeScript
Record<K, V> no TypeScript: objetos de consulta tipados | We.Inc
Monte um tipo Record<K, V> com as suas chaves e veja o objeto que ele descreve. Saiba quando Record supera uma index signature ou um Map, e quais erros ele pega.
Record<K, V> é um utility type nativo para um objeto cujas chaves vêm de K e cujos valores têm todos o tipo V. Passe uma união de string literals e o compilador exige que todas as chaves listadas estejam presentes. Passe string simples e ele se comporta como um dicionário aberto, igual a uma index signature.
Record em resumo
Você escreve
Significa
Pode faltar chave?
Record<"a" | "b", number>
{ a: number; b: number }
Não, as duas chaves são obrigatórias
Record<string, number>
{ [key: string]: number }
Sim, qualquer chave string
Partial<Record<"a" | "b", number>>
{ a?: number; b?: number }
Sim, cada chave é opcional
Record<number, string>
{ [key: number]: string }
Sim, qualquer chave number
Digite as chaves que quiser e escolha um tipo de valor. O gerador escreve o tipo Record, o tipo de objeto simples em que ele se expande e um objeto inicial com uma linha por chave, para você ver exatamente o que o compilador vai cobrar antes de colar no seu código.
Digite as chaves separadas por vírgulas, por exemplo home, about, contact.
Escolha o tipo de valor que cada chave deve guardar.
Marque opcional se algumas chaves puderem ficar de fora, o que envolve o tipo em Partial.
Copie o tipo e o objeto inicial para o seu arquivo e preencha os valores.
Derive a união de chaves de dados que você já tem: keyof typeof someObject ou (typeof list)[number] depois de um array as const mantém o Record em sincronia com a origem.
Use Record<string, T> só quando as chaves realmente chegam em tempo de execução. Uma união fechada pega erros de digitação em nomes de propriedades que um dicionário aberto deixa passar.
Ative noUncheckedIndexedAccess se você lê de um Record<string, T> por uma chave calculada. As leituras passam a retornar T | undefined, o que corresponde ao que acontece em tempo de execução.
Evite Record<string, any>. Record<string, unknown> ainda aceita qualquer objeto, mas obriga a uma verificação antes de usar um valor.
Perguntas frequentes
Record é o mesmo que uma index signature?
Com uma chave ampla como string, sim: Record<string, number> e { [key: string]: number } descrevem o mesmo formato. A diferença aparece com chaves literais. Uma index signature não consegue exigir que nomes específicos existam, enquanto Record<"a" | "b", number> exige a e b.
Quando devo usar um Map em vez de um Record?
Use um Map quando chaves são adicionadas e removidas com frequência, quando as chaves não são strings ou quando você precisa da contagem de entradas por size. Use um Record para um conjunto fixo de nomes, para objetos de configuração e para tudo que você envia por JSON.stringify.
Por que o TypeScript diz que falta uma propriedade no meu Record?
Porque o tipo da chave é uma união e o objeto deixa um membro de fora. O tsc informa, por exemplo, Property 'about' is missing in type '{ home: string; }'. Adicione a chave, ou envolva o tipo em Partial se deixá-la de fora for válido.
Record existe em tempo de execução?
Não. É só um tipo e desaparece quando o código é compilado. O objeto que você cria é um objeto JavaScript comum, então o compilador não consegue impedir que uma resposta do servidor tenha chaves a mais ou a menos.
Qual versão do TypeScript adicionou o Record?
O Record chegou no TypeScript 2.1, junto com Partial, Readonly e Pick, então qualquer compilador atual o tem.
Como o Record é construído
Record é um mapped type. Ele percorre cada membro do tipo de chave e dá a todos o mesmo tipo de valor, e é por isso que Record<"a" | "b", number> se expande para um tipo de objeto com duas propriedades number obrigatórias.
Como é só um tipo, você pode passar qualquer coisa que possa ser chave de propriedade: string literals, number, string, symbol ou uma união deles. Um tipo de chave derivado de outro tipo, como keyof User, se atualiza sozinho quando User muda.
Chaves fechadas versus chaves abertas
O valor do Record está na forma fechada. Uma tabela de consulta de nomes de rotas, rótulos de status ou cores de tema tipada com uma união de literais quebra o build no dia em que alguém adiciona um status novo sem rótulo, que é exatamente quando você quer saber disso.
A forma aberta, Record<string, T>, descreve um dicionário. Ela não diz nada sobre quais chaves existem, então ler uma chave retorna T mesmo quando a chave não existe. Combine com noUncheckedIndexedAccess ou com um Map quando essa lacuna importar.
Record, index signature ou Map
Escolha Record com chaves literais para consultas fixas, Record<string, T> ou uma index signature para dicionários no formato de JSON, e Map para coleções que mudam enquanto o programa roda. O guia de Map no TypeScript deste site aborda esse último caso em profundidade.
Padrões que você vai usar mais
Chaves a partir de um array as const
O array é a única fonte de verdade. Adicione um status a ele e o compilador aponta para a tabela de rótulos até você adicionar o rótulo também.
Uma flag por campo de outro tipo
keyof transforma um tipo existente na união de chaves, então o objeto de estado touched de um formulário sempre lista os mesmos campos que o formulário.
Um dicionário aberto, lido com segurança
Com noUncheckedIndexedAccess ativado, uma leitura por chave calculada é number | undefined, então o fallback é obrigatório em vez de esquecido.
Gerador de tipo Record
Chaves, separadas por vírgulas
Tipo de valor
Chaves opcionais (envolver em Partial)
O tipo
No que ele se expande
Um objeto inicial
Digite pelo menos uma chave. As chaves precisam começar com uma letra, _ ou $.