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:
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:
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):
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
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"
}));
Saída:
Charlie,20 years old,Email:xiaoming@example.com
2. Propriedades opcionais
Use ? para indicar um atributo opcional — esse atributo pode ser omitido:
(1) Sintaxe
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:
// 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
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:
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
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!
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
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
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:
// ❌ 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+)
let css: { [key: `--${string}`]: string } = {
"--primary-color": "#4A90D9",
"--font-size": "14px"
};
▶ Exemplo: Implementação de configuração dinâmica usando assinaturas de índice
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}`);
Saída:
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
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
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"
6. Dois estilos de tipos de objeto
(1) Tipos anônimos (inline)
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)
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
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
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:
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 };
{ 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
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"
Saída:
// Executed successfully
❓ Perguntas Frequentes
P: Os tipos de objeto devem usar os aliases
interfaceoutype? 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çaextends), enquantotypeé 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 cominterface.
P: Qual é a diferença entre propriedades opcionais e o tipo de união
undefined? R:age?: numberpermite que as propriedades estejam ausentes (faltem completamente), enquantoage: number | undefinedexige que as propriedades existam, mas seus valores podem serundefined. No desenvolvimento prático, as propriedades opcionais são mais comumente usadas — omitir uma propriedade parece mais natural do que escrever explicitamenteundefined.
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
readonlysã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 tipoany. 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
- Descrição do tipo de objeto: “Quais propriedades o objeto possui e quais são seus tipos?” Use a sintaxe
{ propertyName: type }. ?indica uma propriedade opcional;readonlyindica uma propriedade somente leitura; “readonly” indica somente leitura superficial- A assinatura de índice
[key: type]: valuetypedescreve um objeto em que “o tipo da chave não é especificado, mas o tipo do valor é especificado” - Objetos aninhados descrevem tipos camada por camada; se houver mais de 2 ou 3 camadas, recomenda-se extraí-las para interfaces ou tipos separados.
- O TypeScript utiliza um sistema de tipos estrutural — se as estruturas corresponderem, elas são compatíveis; a atribuição direta de literais de objeto inclui verificações de propriedades redundantes.
📝 Exercícios
- 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.
- 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. Onamedo usuário original deve ser imutável (usereadonly). - Desafio (Dificuldade ⭐⭐⭐): Defina um tipo
CacheStoreusando 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 deCacheStore<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).