Menu

Tipos de objeto em TypeScript: propriedades opcionais e readonly

Como tipar objetos em TypeScript: tipos de objeto inline, propriedades opcionais com ?, propriedades readonly, objetos aninhados, métodos, excess property checks e a diferença entre object, {} e Object.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Um tipo de objeto em TypeScript lista as propriedades que um objeto tem e o tipo de cada uma: { name: string; age: number }. Adicione ? para tornar uma propriedade opcional e readonly para impedir que ela seja reatribuída. Escreva o tipo inline ou dê um nome a ele com type ou interface para reutilizar.

Escrevendo tipos de objeto

As propriedades são separadas por ; ou , (os dois funcionam, ; é o estilo mais comum), e uma quebra de linha sozinha também basta. Um tipo inline serve para um parâmetro isolado; para qualquer coisa usada duas vezes, dê um nome.

// Inline, in a parameter
function area(rect: { width: number; height: number }): number {
    return rect.width * rect.height;
}

// Named with a type alias
type Rect = { width: number; height: number };

// Named with an interface (the same shape)
interface RectShape {
    width: number;
    height: number;
}

type e interface descrevem formatos de objeto igualmente bem. As diferenças (declaration merging, unions) estão na página interface vs type.

Acessar uma propriedade que o tipo não declara é erro de compilação: point.z dá TS2339, Property 'z' does not exist on type '{ x: number; y: number; }'.

Propriedades opcionais

Um ? depois do nome permite omitir a propriedade. Ler uma propriedade opcional dá T | undefined, então o TypeScript obriga você a tratar o caso ausente antes de usá-la.

Chamar um método em uma propriedade opcional sem verificação é erro: p.nickname.toUpperCase() falha com TS18048, 'p.nickname' is possibly 'undefined'. Use optional chaining (p.nickname?.toUpperCase()) quando undefined for um resultado aceitável.

prop?: T e prop: T | undefined não são a mesma coisa. O primeiro permite que a chave esteja ausente; o segundo exige a chave, embora o valor possa ser undefined:

Propriedades readonly

readonly impede que uma propriedade seja reatribuída depois que o objeto é criado. É uma verificação só de tempo de compilação, e é rasa: um objeto ou array guardado em uma propriedade readonly ainda pode ser alterado por dentro.

A saída mostra os dois limites: o id mudou de verdade em tempo de execução (só o compilador sabia que era readonly), e o array interno foi modificado. Para um array readonly use readonly string[]; para tornar todas as propriedades readonly de uma vez, use Readonly<Order>.

Excess property checks

Quando você atribui um objeto literal diretamente a uma variável tipada ou o passa direto para um parâmetro tipado, o TypeScript rejeita qualquer propriedade que o tipo não declara. Propriedades extras em um literal novo quase sempre são erros de digitação.

index.ts(8,8): error TS2561: Object literal may only specify known properties, but 'colour' does not exist in type 'Options'. Did you mean to write 'color'?

O código é TS2561 porque o compilador encontrou um nome parecido; uma propriedade extra sem nome semelhante dá TS2353, Object literal may only specify known properties, and 'z' does not exist in type 'Point'. Sem essa verificação, o erro de digitação compilaria, color seria undefined e o programa desenharia em preto sem avisar. A verificação vale só para literais novos. Um objeto que já está em uma variável pode ter propriedades extras, porque a tipagem do TypeScript é estrutural: um valor se encaixa em um tipo quando tem pelo menos as propriedades exigidas.

Objetos aninhados e métodos

Tipos de objeto podem ser aninhados e podem descrever métodos com a sintaxe de método ou com uma propriedade de tipo função.

Para formatos profundos ou reutilizados, dê nome ao tipo interno (type Address = { ... }) e faça referência a ele, ou extraia-o do tipo externo com um indexed access, Company["address"], como fazem as últimas linhas.

object vs {} vs Object

Três tipos com nomes parecidos significam coisas diferentes:

TipoAceitaRejeita
objectqualquer não primitivo: {}, [], funções, instâncias de classe5, "a", true, null, undefined
{}qualquer valor exceto null e undefined, primitivos incluídosnull, undefined
Objecto mesmo que {}, mais uma verificação de que membros nativos como toString mantêm tipos compatíveisnull, undefined
{ x: number }qualquer valor com um x numéricovalores sem x

{} não significa "um objeto vazio"; significa "não é null nem undefined". Para aceitar qualquer objeto com chaves desconhecidas, use Record<string, unknown>; para um mapa de chaves e valores, use uma index signature ou Record, como mostra a página de dicionário. Na maioria das vezes, um formato específico é melhor que qualquer um dos três.

Perguntas frequentes

Como definir um tipo de objeto no TypeScript?

Liste as propriedades e seus tipos entre chaves: { name: string; age: number }. Você pode escrever inline em uma anotação ou dar um nome com type User = { ... } ou interface User { ... } e reutilizar. Separe as propriedades com ; ou ,.

Como tornar uma propriedade opcional no TypeScript?

Coloque ? depois do nome da propriedade: { name: string; nickname?: string }. O objeto pode omitir nickname, e lê-la dá string | undefined, então você precisa verificar ou fornecer um valor padrão (user.nickname ?? user.name) antes de usá-la como string.

Qual é a diferença entre prop?: string e prop: string | undefined?

Com prop?: string a chave pode ser omitida por completo. Com prop: string | undefined a chave é obrigatória, embora o valor possa ser undefined, então {} é um erro de compilação. Ler qualquer uma das duas dá string | undefined.

Qual é a diferença entre object, {} e Object no TypeScript?

object significa qualquer valor não primitivo (objetos, arrays, funções) e rejeita 5 ou "a". {} significa qualquer valor exceto null e undefined, primitivos incluídos. Object é quase igual a {}, mas também verifica se métodos nativos como toString mantêm tipos compatíveis. Use object, ou melhor ainda um formato específico, em vez de {} ou Object.

Por que o TypeScript reclama que um objeto literal só pode especificar propriedades conhecidas?

É o excess property check: erro TS2353, ou TS2561 quando o compilador consegue sugerir a propriedade que você provavelmente quis escrever. Quando você atribui um objeto literal novo diretamente a uma variável ou parâmetro tipado, qualquer propriedade que o tipo não declara é apontada, porque em geral é um erro de digitação. Atribuir um objeto guardado em outra variável não passa por essa verificação, já que a tipagem estrutural permite propriedades extras.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR