ZetCode

TypeScript 对象类型

最后修改于 2025 年 3 月 5 日

TypeScript 通过为对象添加静态类型来增强 JavaScript。对象类型定义了对象的结构,确保了类型安全。本教程将通过实际示例探讨对象类型的转换。

基本对象类型

TypeScript 允许使用接口或类型别名来定义对象类型。本示例展示了一种基本对象类型。

basic_object.ts
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` 语句按预期输出对象。

可选属性

属性可以使用 `?` 符号标记为可选。这为对象结构提供了灵活性。

optional_properties.ts
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`,以防止初始化后进行修改。

readonly_properties.ts
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 可确保嵌套结构的类型安全。

nested_objects.ts
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 确保嵌套结构具有正确的类型,并且输出反映了完整的对象层次结构。

交叉类型

交叉类型将多个类型合并为一个。这对于扩展对象类型很有用。

intersection_types.ts
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 会强制要求同时存在这两个属性。输出按预期显示了合并后的对象。

联合类型

联合类型允许对象是几种类型之一。这为对象结构提供了灵活性。

union_types.ts
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 的推断类型。请谨慎使用此功能以确保类型安全。

type_assertion.ts
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` 具有预期的属性,因此有效。输出确认了对象的结构。

索引签名

索引签名允许对象具有动态属性名。这对于字典或映射很有用。

index_signatures.ts
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 确保所有值都是数字。输出显示了生成的字典状对象。

映射类型

映射类型转换对象属性。这对于从现有类型创建新类型很有用。

mapped_types.ts
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` 对象不能修改其属性,正如注释掉的错误所示。这是动态强制执行不变性的强大方法。

最佳实践

来源

TypeScript 对象类型文档

本教程通过实际示例介绍了 TypeScript 对象类型。使用这些模式编写更安全、更易于维护的代码。

作者

我的名字是 Jan Bodnar,我是一名充满热情的程序员,拥有丰富的编程经验。我从 2007 年开始撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有超过十年的经验。

列出所有 TypeScript 教程