TypeScript: Tipos de objetos no TypeScript

Última atualização: 2026-08-26

Em JavaScript, os objetos são as estruturas de dados mais utilizadas. O tipo “objeto” do TypeScript permite descrever com precisão “quais propriedades um objeto deve ter e quais são seus tipos” — isso está no cerne de seu sistema de tipos estruturado.

1. Noções básicas sobre tipos de objetos

(1) Tipos de objetos anônimos

A abordagem mais direta — especificar a estrutura do objeto diretamente na declaração do tipo:

TYPESCRIPT
let user: { name: string; age: number } = {
  name: "Charlie",
  age: 20
};

(2) Inferência de tipos

Quando um literal de objeto é atribuído a uma variável, o TypeScript infere automaticamente o tipo:

TYPESCRIPT
let user = {
  name: "Charlie",
  age: 20
};
// Inferred as { name: string; age: number }

user.name = "Diana";   // ✅
user.age = 21;        // ✅
// user.email = "x@x.com";  // ❌ Not included in the types of inferences email Properties

(3) Sintaxe em várias linhas

Quando um objeto possui muitas propriedades, recomenda-se usar um formato de várias linhas (separadas por ponto-e-vírgula ou vírgulas):

TYPESCRIPT
let product: {
  id: number;
  name: string;
  price: number;
  inStock: boolean;
} = {
  id: 1,
  name: "TypeScriptAuthoritative Guide",
  price: 89.9,
  inStock: true
};

▶ Exemplo: Tipos de objetos para parâmetros de função

TYPESCRIPT
function createUser(user: { name: string; age: number; email: string }): string {
  return `${user.name},${user.age} years old,Email:${user.email}`;
}

console.log(createUser({
  name: "Charlie",
  age: 20,
  email: "xiaoming@example.com"
}));
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Charlie,20 years old,Email:xiaoming@example.com


2. Propriedades opcionais

Use ? para indicar um atributo opcional — esse atributo pode ser omitido:

(1) Sintaxe

TYPESCRIPT
let user: { name: string; age?: number } = {
  name: "Charlie"
  // age This information is optional.
};

console.log(user.name);  // "Charlie"
console.log(user.age);   // undefined

(2) Tipos de atributos opcionais

Os tipos de atributos opcionais incluem automaticamente undefined:

TYPESCRIPT
// age?: number Equivalent to age: number | undefined
// But there is an important difference:age?: number The property does not exist.,age: number | undefined The property must exist.

let config: { host: string; port?: number } = { host: "localhost" };
// config.port is undefined(The property does not exist either. ok 's)

let config2: { host: string; port: number | undefined } = { host: "localhost" };
// ❌ Error! port property must exist,Even if undefined You must also explicitly write
// Correct Way to Write It:{ host: "localhost", port: undefined }

(3) Métodos seguros para acessar atributos opcionais

TYPESCRIPT
interface User {
  name: string;
  age?: number;
}

function getAgeText(user: User): string {
  // ❌ Unsafe——If age Does not exist,undefined.toFixed() It crashes during runtime
  // return user.age.toFixed(0) + " years old";

  // ✅ Method 1:Check first
  if (user.age !== undefined) {
    return user.age.toFixed(0) + " years old";
  }
  return "Age unknown";

  // ✅ Method 2:Provide a default value
  // return (user.age ?? 0).toFixed(0) + " years old";
}


3. Propriedades somente leitura

Use a tag readonly para marcar uma propriedade como somente leitura — ela não poderá ser modificada após a atribuição:

TYPESCRIPT
let user: { readonly id: number; name: string } = {
  id: 1,
  name: "Charlie"
};

user.name = "Diana";     // ✅ General attributes can be modified
// user.id = 2;         // ❌ Read-only properties cannot be modified.

(2) readonly é um modificador superficial somente para leitura

TYPESCRIPT
let config: { readonly data: { value: number } } = {
  data: { value: 42 }
};

// config.data = { value: 100 };  // ❌ data Cannot be reassigned
config.data.value = 100;          // ✅ data The internal properties can still be modified!
💡 Somente leitura profunda: Se você precisar que todos os níveis de um objeto sejam somente de leitura, use o tipo de ferramenta Readonly<type> (aninhado) ou a asserção as const. A lição 19 abordará os tipos de ferramenta Readonly, Partial, Required e outros.



4. Assinaturas de índice

Quando o nome da propriedade de um objeto é desconhecido, mas o tipo do valor da propriedade é conhecido, use uma assinatura de índice:

(1) Assinatura de índice de string

TYPESCRIPT
let scores: { [key: string]: number } = {
  Chinese Language: 90,
  Mathematics: 95,
  English: 88
};

console.log(scores["Mathematics"]);   // 95

// Add a New Property——As long as it is string key + number Any value will do.
scores["Physics"] = 85;   // ✅

(2) Assinaturas digitais de índice

TYPESCRIPT
let arr: { [index: number]: string } = {
  0: "first",
  1: "second",
  2: "third"
};

console.log(arr[1]);  // "second"

(3) Limitações às assinaturas de índice

A assinatura do índice exige que os tipos de todos os atributos conhecidos sejam subtipos do tipo do valor do índice:

TYPESCRIPT
// ❌ Error: name is string,However, index signatures require that all values be number
// let config: { [key: string]: number; name: string } = { name: "test" };

// ✅ Correct:All value types are consistent
let config: { [key: string]: string; name: string } = { name: "test" };

(4) Assinaturas de índice de literais de modelo (TypeScript 4.4+)

TYPESCRIPT
let css: { [key: `--${string}`]: string } = {
  "--primary-color": "#4A90D9",
  "--font-size": "14px"
};

▶ Exemplo: Implementação de configuração dinâmica usando assinaturas de índice

TYPESCRIPT
interface EnvConfig {
  [key: string]: string | number | boolean;
  // All values can be string、number or boolean
  // Known properties must also be compatible.
  NODE_ENV: string;
  PORT: number;
}

let env: EnvConfig = {
  NODE_ENV: "development",
  PORT: 3000,
  DEBUG: true,        // ✅ Dynamic Properties
  API_URL: "http://localhost:8080"  // ✅ Dynamic Properties
};

console.log(`Environment:${env.NODE_ENV}`);
console.log(`Port:${env.PORT}`);
console.log(`Debugging:${env.DEBUG}`);
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Environment:development
Port:3000
Debugging:true


5. Tipos de objetos aninhados

No desenvolvimento prático, os objetos costumam estar aninhados em vários níveis — o TypeScript permite descrever tipos camada por camada:

(1) Tipos de literais de objeto aninhados

TYPESCRIPT
let user: {
  name: string;
  address: {
    city: string;
    zip: string;
  };
  tags: string[];
} = {
  name: "Charlie",
  address: {
    city: "Beijing",
    zip: "100000"
  },
  tags: ["Developer", "TypeScript"]
};

console.log(user.address.city);  // "Beijing"
console.log(user.tags[0]);       // "Developer"

(2) Aninhamento em vários níveis

TYPESCRIPT
type ApiResponse = {
  status: number;
  data: {
    user: {
      id: number;
      profile: {
        avatar: string;
        bio: string;
      };
    };
  };
};

let response: ApiResponse = {
  status: 200,
  data: {
    user: {
      id: 1,
      profile: {
        avatar: "https://example.com/avatar.png",
        bio: "TypeScript Enthusiast"
      }
    }
  }
};

console.log(response.data.user.profile.bio);  // "TypeScript Enthusiast"
💡 Dica: Quando o aninhamento ultrapassar 2–3 níveis, você deve extrair os tipos internos para interfaces separadas ou aliases de tipo, a fim de melhorar a legibilidade e a reutilização. Esse assunto será abordado em detalhes nas Lições 9–11.



6. Dois estilos de tipos de objeto

(1) Tipos anônimos (inline)

TYPESCRIPT
function greet(user: { name: string; age: number }): string {
  return `Hello,${user.name}!`;
}

Adequado para: tipos simples usados apenas uma vez, parâmetros de função

(2) Tipos de nomenclatura (interface / tipo)

TYPESCRIPT
interface User {
  name: string;
  age: number;
}

function greet(user: User): string {
  return `Hello,${user.name}!`;
}

Adequado para: múltiplas utilizações, diversas propriedades e situações que exijam herança ou extensão

📌 Recomendação: Se houver mais de três propriedades ou se você precisar reutilizar um tipo, extraia-o para uma interface ou um alias de tipo. Para parâmetros simples que serão usados apenas uma vez, use um tipo anônimo.



7. Sistemas de tipos estruturados

O TypeScript utiliza um sistema de tipos estruturado — desde que a estrutura de um objeto atenda aos requisitos de tipo, ele é compatível, independentemente de os nomes dos tipos corresponderem:

(1) Correspondência estrutural

TYPESCRIPT
interface Point {
  x: number;
  y: number;
}

let point: Point = { x: 1, y: 2 };   // ✅ Has x and y

let point3D = { x: 1, y: 2, z: 3 };
point = point3D;   // ✅ point3D has x and y,Redundant attributes do not conflict

(2) Verifique se há atributos desnecessários

Ao atribuir um valor diretamente a um objeto literal, o TypeScript realiza uma “verificação de propriedades redundantes” — ele não permite propriedades que não existam no tipo de destino:

TYPESCRIPT
interface Point {
  x: number;
  y: number;
}

// ❌ Check for Unnecessary Attributes——No additional properties are allowed when directly assigning an object literal.
// let p: Point = { x: 1, y: 2, z: 3 };

// ✅ Method 1:Bypassing via Variables(Because variables do not trigger unnecessary property checks)
let temp = { x: 1, y: 2, z: 3 };
let p: Point = temp;   // ✅

// ✅ Method 2:Using Type Assertions
let p2: Point = { x: 1, y: 2, z: 3 } as Point;

// ✅ Method 3:Extension Interface(Top Recommendations)
interface Point3D extends Point {
  z: number;
}
let p3: Point3D = { x: 1, y: 2, z: 3 };
💡 Por que verificar se há propriedades extras? Isso serve para detectar erros ortográficos. Se você escrever { x: 1, y: 2, zzz: 3 }, o TypeScript irá alertá-lo imediatamente — “zzz” não é uma propriedade de Point; será que você escreveu errado?

▶ Exemplo: Tipos estruturados na prática

TYPESCRIPT
interface Printable {
  toString(): string;
}

// Date has toString method,Structural Matching
let date: Printable = new Date();

// Custom objects include toString Methods,Also matches
let custom: Printable = {
  toString() { return "Custom Objects"; }
};

function print(obj: Printable): void {
  console.log(obj.toString());
}

print(date);    // Output Date the string representation of
print(custom);  // Output "Custom Objects"
▶ Experimente

Saída:

TEXT 📖 Somente leitura
// Executed successfully

❓ Perguntas Frequentes

P: Os tipos de objeto devem usar os aliases interface ou type? R: Na maioria dos casos, os dois são equivalentes. interface é mais adequado para definir formas de objetos (suportando fusão de declarações e herança extends), enquanto type é mais adequado para operações complexas, como tipos de união e tipos condicionais. Desde que sua equipe utilize uma abordagem consistente, não há problema. Uma comparação detalhada será apresentada mais adiante neste capítulo. Recomenda-se que iniciantes comecem com interface.

P: Qual é a diferença entre propriedades opcionais e o tipo de união undefined? R: age?: number permite que as propriedades estejam ausentes (faltem completamente), enquanto age: number | undefined exige que as propriedades existam, mas seus valores podem ser undefined. No desenvolvimento prático, as propriedades opcionais são mais comumente usadas — omitir uma propriedade parece mais natural do que escrever explicitamente undefined.

P: Por que os literais de objeto passam por verificações adicionais de propriedades, mas as variáveis não? R: Ao atribuir valores diretamente a literais de objeto, os desenvolvedores geralmente sabem exatamente o que escreveram — propriedades adicionais são, muito provavelmente, erros de digitação. Ao atribuir valores a variáveis, o tipo da variável pode vir de outra fonte, e propriedades adicionais podem ser válidas (como propriedades adicionais de um subtipo). Essa é a equação entre “segurança” e “flexibilidade” do TypeScript.

P: Os valores com o atributo readonly são realmente imutáveis? R: readonly é apenas uma verificação em tempo de compilação; não há proteção em tempo de execução. É possível contorná-la usando asserções de tipo ou o tipo any. No entanto, ele impede efetivamente “modificações acidentais” — o que é o objetivo principal do sistema de tipos: detectar erros em tempo de compilação, em vez de fornecer proteção em tempo de execução.

📖 Resumo

📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Defina um tipo de objeto para representar um “livro” (título, autor, preço, em estoque?), crie dois objetos “livro” — um com o campo “em estoque” e outro sem — e exiba seus títulos e preços.
  2. Problema avançado (Dificuldade ⭐⭐): Escreva uma função updateUser(user: { name: string; age: number; email?: string }, updates: { age?: number; email?: string }) que retorne o objeto usuário atualizado. O name do usuário original deve ser imutável (use readonly).
  3. Desafio (Dificuldade ⭐⭐⭐): Defina um tipo CacheStore usando assinaturas de índice, em que a chave seja uma string e o valor seja { data: T; timestamp: number } (T é um tipo genérico). Crie uma instância de CacheStore<string>, armazene duas entradas de cache nela e, em seguida, percorra e exiba todas as chaves armazenadas em cache e seus status de expiração (supondo uma expiração de 5 segundos).
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%