TypeScript: TypeScript Object Types
Last updated: 2026-08-26
In JavaScript, objects are the most commonly used data structures. TypeScript’s object type allows you to precisely describe “what properties an object should have and what their types are”—this is at the core of its structured type system.
1. Object Type Basics
(1) Anonymous Object Types
The most straightforward approach—specifying the object's structure directly at the type declaration:
let user: { name: string; age: number } = {
name: "Charlie",
age: 20
};
(2) Type Inference
When an object literal is assigned to a variable, TypeScript automatically infers the type:
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) Multi-line Syntax
When an object has many properties, it is recommended to use a multi-line format (separated by semicolons or commas):
let product: {
id: number;
name: string;
price: number;
inStock: boolean;
} = {
id: 1,
name: "TypeScriptAuthoritative Guide",
price: 89.9,
inStock: true
};
▶ Example: Object Types for Function Parameters
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"
}));
Output:
Charlie,20 years old,Email:xiaoming@example.com
2. Optional Properties
Use ? to mark an optional attribute—this attribute may be omitted:
(1) Syntax
let user: { name: string; age?: number } = {
name: "Charlie"
// age This information is optional.
};
console.log(user.name); // "Charlie"
console.log(user.age); // undefined
(2) Types of Optional Attributes
The types of optional attributes automatically include 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) Secure Methods for Accessing Optional Attributes
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. Read-Only Properties
Use the readonly tag to mark a property as read-only—it cannot be modified after assignment:
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 is a shallow read-only modifier
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> tool type (nested) or the as const assertion. Lesson 19 will cover the Readonly, Partial, Required, and other tool types.
4. Index Signatures
When the property name of an object is unknown but the type of the property value is known, use an index signature:
(1) String Index Signature
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) Digital Index Signatures
let arr: { [index: number]: string } = {
0: "first",
1: "second",
2: "third"
};
console.log(arr[1]); // "second"
(3) Limitations on Index Signatures
The index signature requires that the types of all known attributes must be subtypes of the index value type:
// ❌ 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) Template literal index signatures (TypeScript 4.4+)
let css: { [key: `--${string}`]: string } = {
"--primary-color": "#4A90D9",
"--font-size": "14px"
};
▶ Example: Implementing Dynamic Configuration Using Index Signatures
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}`);
Output:
Environment:development
Port:3000
Debugging:true
5. Nested Object Types
In actual development, objects are often nested in multiple levels—TypeScript supports describing types layer by layer:
(1) Types of nested object literals
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) Multi-level nesting
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. Two Styles of Object Types
(1) Anonymous Types (Inline)
function greet(user: { name: string; age: number }): string {
return `Hello,${user.name}!`;
}
Suitable for: simple types used only once, function parameters
(2) Naming Types (interface / type)
interface User {
name: string;
age: number;
}
function greet(user: User): string {
return `Hello,${user.name}!`;
}
Suitable for: Multiple uses, numerous properties, and situations requiring inheritance or extension
7. Structured Type Systems
TypeScript uses a structured type system—as long as an object’s structure meets the type requirements, it is compatible, regardless of whether the type names match:
(1) Structural Matching
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) Check for Unnecessary Attributes
When assigning a value directly to an object literal, TypeScript performs a "redundant property check"—it does not allow properties that do not exist in the target type:
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 }, TypeScript will immediately alert you—zzz isn’t a property of Point; did you misspell it?
▶ Example: Structured Types in Practice
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"
Output:
<current date ISO string>
Custom Objects
❓ FAQ
interface or type aliases?interface is better suited for defining object shapes (supporting declaration merging and extends inheritance), while type is better suited for complex operations such as union types and conditional types. As long as your team uses a consistent approach, it’s fine. A detailed comparison will follow later in this chapter. Beginners are recommended to start with interface.undefined union type?age?: number allows properties to be absent (completely missing), while age: number | undefined requires that properties must exist but their values can be undefined. In actual development, optional properties are more commonly used—omitting a property feels more natural than explicitly writing undefined.readonly attribute truly immutable?readonly is only a compile-time check; there is no runtime protection. It can be bypassed using type assertions or the any type. However, it effectively prevents "accidental modifications"—which is the primary purpose of the type system: to catch errors at compile time, rather than providing runtime protection.📖 Summary
- Object type description: "What properties does the object have, and what are their types?" Use the
{ propertyName: type }syntax. ?marks an optional property;readonlymarks a read-only property; "readonly" indicates shallow read-only- Index signature
[key: type]: valuetypedescribes an object where "the key type is unspecified but the value type is specified" - Nested objects describe types layer by layer; if there are more than 2–3 layers, it is recommended to extract them into separate interfaces or types.
- TypeScript uses a structural type system—if the structures match, they are compatible; direct assignment of object literals includes checks for redundant properties.
📝 Exercises
- Basic Problem (Difficulty ⭐): Define an object type to represent a "book" (title, author, price, inStock?), create two book objects—one with inStock and one without—and print their titles and prices.
- Advanced Problem (Difficulty ⭐⭐): Write a function
updateUser(user: { name: string; age: number; email?: string }, updates: { age?: number; email?: string })that returns the updated user object. The original user'snamemust be immutable (usereadonly). - Challenge (Difficulty ⭐⭐⭐): Define a type
CacheStoreusing index signatures, where the key is a string and the value is{ data: T; timestamp: number }(T is a generic type). Create an instance ofCacheStore<string>, store two cache entries in it, and then iterate through and output all cached keys and their expiration statuses (assuming a 5-second expiration).