TypeScript 对象类型
最后修改于 2025 年 3 月 5 日
TypeScript 通过为对象添加静态类型来增强 JavaScript。对象类型定义了对象的结构,确保了类型安全。本教程将通过实际示例探讨对象类型的转换。
基本对象类型
TypeScript 允许使用接口或类型别名来定义对象类型。本示例展示了一种基本对象类型。
type User = {
name: string;
age: number;
};
const user: User = { name: "Alice", age: 25 };
console.log(user); // Output: { name: 'Alice', age: 25 }
在此示例中,`User` 类型使用类型别名定义,具有两个属性:`name`(类型为 `string`)和 `age`(类型为 `number`)。然后将 `user` 变量赋值为一个符合此类型的对象。TypeScript 在编译时检查对象是否与定义的结构匹配,从而确保类型安全。`console.log` 语句按预期输出对象。
可选属性
属性可以使用 `?` 符号标记为可选。这为对象结构提供了灵活性。
type Product = {
id: number;
name: string;
price?: number;
};
const product: Product = { id: 1, name: "Laptop" };
console.log(product); // Output: { id: 1, name: 'Laptop' }
在此,`Product` 类型包含一个可选的 `price` 属性,由 `?` 符号表示。这意味着 `Product` 类型的对象必须具有 `id` 和 `name`,但可以省略 `price`。在该示例中,`product` 对象在没有 `price` 的情况下创建,TypeScript 允许这种灵活性,同时仍然强制执行必需的属性。输出显示了没有可选属性的对象。
只读属性
属性可以标记为 `readonly`,以防止初始化后进行修改。
type Config = {
readonly apiKey: string;
readonly endpoint: string;
};
const config: Config = { apiKey: "12345", endpoint: "/api" };
// config.apiKey = "67890"; // Error: Cannot assign to 'apiKey'
在此示例中,`Config` 类型对 `apiKey` 和 `endpoint` 都使用了 `readonly` 修饰符。一旦 `config` 对象被初始化,这些属性就不能被更改。注释掉的行表明尝试重新分配 `apiKey` 会导致 TypeScript 错误,从而强制执行不变性。这对于确保关键配置值在程序中保持不变非常有用。
嵌套对象
对象可以包含嵌套对象。TypeScript 可确保嵌套结构的类型安全。
type Address = {
city: string;
zipCode: string;
};
type Person = {
name: string;
address: Address;
};
const person: Person = {
name: "Bob",
address: { city: "New York", zipCode: "10001" }
};
console.log(person); // Output: { name: 'Bob', address: { city: 'New York', zipCode: '10001' } }
此示例演示了嵌套对象类型。`Address` 类型定义了一个具有 `city` 和 `zipCode` 的结构,然后该结构被用作 `Person` 类型中的属性类型。`person` 对象包含一个符合 `Address` 类型的嵌套 `address` 对象。TypeScript 确保嵌套结构具有正确的类型,并且输出反映了完整的对象层次结构。
交叉类型
交叉类型将多个类型合并为一个。这对于扩展对象类型很有用。
type Name = { name: string };
type Age = { age: number };
type Person = Name & Age;
const person: Person = { name: "Alice", age: 25 };
console.log(person); // Output: { name: 'Alice', age: 25 }
交叉类型使用 `&` 运算符创建,将 `Name` 和 `Age` 类型合并为单个 `Person` 类型。这意味着 `Person` 必须同时拥有这两个类型的属性:`name` 和 `age`。`person` 对象满足此要求,TypeScript 会强制要求同时存在这两个属性。输出按预期显示了合并后的对象。
联合类型
联合类型允许对象是几种类型之一。这为对象结构提供了灵活性。
type Car = { type: "car"; wheels: number };
type Bike = { type: "bike"; pedals: number };
type Vehicle = Car | Bike;
const vehicle: Vehicle = { type: "car", wheels: 4 };
console.log(vehicle); // Output: { type: 'car', wheels: 4 }
联合类型,用 `|` 运算符表示,允许 `Vehicle` 类型是 `Car` 或 `Bike`。每种类型都有一个区分性的 `type` 属性及其独有的属性(`Car` 为 `wheels`,`Bike` 为 `pedals`)。在这种情况下,`vehicle` 对象符合 `Car` 类型,TypeScript 确保它匹配其中一种允许的类型。输出反映了所选的类型。
类型断言
类型断言允许覆盖 TypeScript 的推断类型。请谨慎使用此功能以确保类型安全。
const data: any = { name: "Alice", age: 25 };
const user = data as { name: string; age: number };
console.log(user); // Output: { name: 'Alice', age: 25 }
此处使用类型断言将 `any` 类型的 `data` 变量转换为具有 `name` 和 `age` 属性的特定对象类型。`as` 关键字告诉 TypeScript 将 `data` 视为指定类型,从而绕过其正常的类型推断。如果实际结构不匹配,这可能会有风险,但在此示例中,由于 `data` 具有预期的属性,因此有效。输出确认了对象的结构。
索引签名
索引签名允许对象具有动态属性名。这对于字典或映射很有用。
type Dictionary = {
[key: string]: number;
};
const scores: Dictionary = { math: 90, science: 85 };
console.log(scores); // Output: { math: 90, science: 85 }
`Dictionary` 类型使用索引签名 `[key: string]: number`,它允许任何字符串键映射到一个数字值。这非常适合属性名称未知于提前的对象,例如键值存储。`scores` 对象为 `math` 和 `science` 键分配了数字,TypeScript 确保所有值都是数字。输出显示了生成的字典状对象。
映射类型
映射类型转换对象属性。这对于从现有类型创建新类型很有用。
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
const readonlyUser: ReadonlyUser = { name: "Alice", age: 25 };
// readonlyUser.name = "Bob"; // Error: Cannot assign to 'name'
映射类型允许通过转换现有 `User` 类型(之前已定义)的属性来创建新类型 `ReadonlyUser`。`keyof User` 操作符获取 `User` 的所有键(`name` 和 `age`),然后将 `readonly` 修饰符应用于每个键。生成的 `readonlyUser` 对象不能修改其属性,正如注释掉的错误所示。这是动态强制执行不变性的强大方法。
最佳实践
- 使用接口:为对象类型优先使用接口
- 类型安全:在编译时验证对象结构
- 避免 any:尽量少用 `any` 以获得更好的类型安全
- 只读:使用 `readonly` 来表示不可变属性
- 文档:添加注释以描述复杂类型
来源
本教程通过实际示例介绍了 TypeScript 对象类型。使用这些模式编写更安全、更易于维护的代码。