对象类型
概述
在 TypeScript 中,对象类型(Object Types)用于描述对象的结构和属性。对象类型是 TypeScript 类型系统的重要组成部分,它允许我们定义对象应该具有哪些属性,以及每个属性的类型。通过对象类型,我们可以在编译时检查对象的结构是否正确,避免运行时错误。
基本语法
对象类型使用花括号 {} 定义,其中包含属性名和类型:
typescript
// 基本对象类型定义
let user: {
name: string;
age: number;
email: string;
};
// 使用对象类型
user = {
name: "John Doe",
age: 30,
email: "john@example.com"
};对象类型特性
必需属性
默认情况下,对象类型中定义的所有属性都是必需的,必须提供且类型匹配:
typescript
// 定义对象类型
type Person = {
name: string;
age: number;
};
// ✅ 正确:提供所有必需属性
const person1: Person = {
name: "Alice",
age: 25
};
// ❌ 错误:缺少必需属性 age
const person2: Person = {
name: "Bob"
// Error: Property 'age' is missing in type '{ name: string; }'
};
// ❌ 错误:类型不匹配
const person3: Person = {
name: "Charlie",
age: "30" // Error: Type 'string' is not assignable to type 'number'
};可选属性
使用 ? 标记属性为可选,表示该属性可以存在也可以不存在:
typescript
// 定义包含可选属性的对象类型
type User = {
name: string;
age: number;
email?: string; // 可选属性
phone?: string; // 可选属性
};
// ✅ 正确:提供所有必需属性
const user1: User = {
name: "Alice",
age: 25
};
// ✅ 正确:提供部分可选属性
const user2: User = {
name: "Bob",
age: 30,
email: "bob@example.com"
};
// ✅ 正确:提供所有属性
const user3: User = {
name: "Charlie",
age: 35,
email: "charlie@example.com",
phone: "123-456-7890"
};只读属性
使用 readonly 关键字标记属性为只读,表示该属性在创建后不能被修改:
typescript
// 定义包含只读属性的对象类型
type Config = {
readonly apiKey: string;
readonly version: string;
timeout: number; // 普通属性,可以修改
};
// 创建对象
const config: Config = {
apiKey: "abc123",
version: "1.0.0",
timeout: 5000
};
// ✅ 正确:可以修改普通属性
config.timeout = 10000;
// ❌ 错误:不能修改只读属性
config.apiKey = "new-key"; // Error: Cannot assign to 'apiKey' because it is a read-only property
config.version = "2.0.0"; // Error: Cannot assign to 'version' because it is a read-only property提示
readonly 只在编译时生效,运行时仍然可以修改。如果需要真正的不可变对象,可以使用 Object.freeze() 或第三方库。
索引签名
当对象的属性名不确定时,可以使用索引签名(Index Signature)来定义:
typescript
// 使用索引签名定义对象类型
type Dictionary = {
[key: string]: string; // 索引签名:所有键都是 string,值也是 string
};
const dict: Dictionary = {
"apple": "苹果",
"banana": "香蕉",
"cherry": "樱桃"
};
// 可以动态添加属性
dict["date"] = "日期";
// ❌ 错误:值类型不匹配
dict["number"] = 123; // Error: Type 'number' is not assignable to type 'string'索引签名也可以与其他属性结合使用:
typescript
// 混合使用固定属性和索引签名
type UserData = {
name: string; // 固定属性
age: number; // 固定属性
[key: string]: string | number; // 索引签名:允许其他 string 或 number 类型的属性
};
const userData: UserData = {
name: "John",
age: 30,
city: "Beijing", // 通过索引签名允许
score: 95 // 通过索引签名允许
};注意
当使用索引签名时,固定属性的类型必须是索引签名类型的子类型。例如,如果索引签名是 [key: string]: string | number,那么所有固定属性的类型必须是 string | number 或其子类型。
使用示例
示例 1:用户信息管理
typescript
// 定义用户对象类型
type User = {
id: number;
name: string;
email: string;
age?: number; // 可选属性
readonly createdAt: Date; // 只读属性
preferences?: {
theme: "light" | "dark";
language: string;
};
};
// 创建用户对象
function createUser(
id: number,
name: string,
email: string,
age?: number
): User {
return {
id,
name,
email,
age,
createdAt: new Date() // 只读属性在创建时设置
};
}
// 使用示例
const user = createUser(1, "Alice", "alice@example.com", 25);
console.log(user.name); // "Alice"
console.log(user.age); // 25
console.log(user.createdAt); // 当前日期
// 可以添加可选属性
user.preferences = {
theme: "dark",
language: "zh-CN"
};示例 2:配置对象
typescript
// 定义应用配置类型
type AppConfig = {
readonly appName: string;
readonly version: string;
api: {
baseUrl: string;
timeout: number;
retries?: number; // 可选的重试次数
};
features: {
[featureName: string]: boolean; // 使用索引签名表示动态特性开关
};
};
// 创建配置对象
const config: AppConfig = {
appName: "MyApp",
version: "1.0.0",
api: {
baseUrl: "https://api.example.com",
timeout: 5000,
retries: 3
},
features: {
darkMode: true,
notifications: false,
analytics: true
}
};
// 可以动态添加特性
config.features["newFeature"] = true;
// ❌ 错误:不能修改只读属性
// config.appName = "NewApp"; // Error示例 3:表单数据验证
typescript
// 定义表单数据类型
type FormData = {
username: string;
email: string;
password: string;
confirmPassword?: string; // 可选属性
rememberMe: boolean;
};
// 验证函数
function validateForm(data: FormData): boolean {
// 检查必需字段
if (!data.username || !data.email || !data.password) {
return false;
}
// 验证邮箱格式(简单示例)
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(data.email)) {
return false;
}
// 如果提供了确认密码,检查是否匹配
if (data.confirmPassword && data.password !== data.confirmPassword) {
return false;
}
return true;
}
// 使用示例
const formData: FormData = {
username: "john_doe",
email: "john@example.com",
password: "secret123",
confirmPassword: "secret123",
rememberMe: true
};
if (validateForm(formData)) {
console.log("表单验证通过");
} else {
console.log("表单验证失败");
}对象类型与接口
对象类型可以使用类型别名(Type Alias)或接口(Interface)来定义。两者在大多数情况下可以互换使用:
typescript
// 使用类型别名定义对象类型
type Point = {
x: number;
y: number;
};
// 使用接口定义对象类型
interface PointInterface {
x: number;
y: number;
}
// 两者使用方式相同
const point1: Point = { x: 10, y: 20 };
const point2: PointInterface = { x: 10, y: 20 };嵌套对象类型
对象类型可以嵌套,用于表示复杂的数据结构:
typescript
// 定义嵌套对象类型
type Address = {
street: string;
city: string;
zipCode: string;
country: string;
};
type Company = {
name: string;
address: Address; // 嵌套对象类型
employees: number;
};
type Employee = {
id: number;
name: string;
email: string;
company: Company; // 嵌套对象类型
address?: Address; // 可选嵌套对象
};
// 使用嵌套对象类型
const employee: Employee = {
id: 1,
name: "Alice",
email: "alice@example.com",
company: {
name: "Tech Corp",
address: {
street: "123 Main St",
city: "San Francisco",
zipCode: "94102",
country: "USA"
},
employees: 100
},
address: {
street: "456 Oak Ave",
city: "San Francisco",
zipCode: "94103",
country: "USA"
}
};类型检查示例
常见错误
typescript
// ❌ 错误:缺少必需属性
type User = {
name: string;
age: number;
};
const user: User = {
name: "John"
// Error: Property 'age' is missing in type '{ name: string; }'
};
// ❌ 错误:类型不匹配
const user2: User = {
name: "John",
age: "30" // Error: Type 'string' is not assignable to type 'number'
};
// ❌ 错误:多余的属性(在严格模式下)
const user3: User = {
name: "John",
age: 30,
email: "john@example.com" // Error: Object literal may only specify known properties
};
// ❌ 错误:修改只读属性
type Config = {
readonly apiKey: string;
};
const config: Config = {
apiKey: "abc123"
};
config.apiKey = "new-key"; // Error: Cannot assign to 'apiKey' because it is a read-only property正确写法
typescript
// ✅ 正确:提供所有必需属性
type User = {
name: string;
age: number;
email?: string; // 可选属性
};
const user: User = {
name: "John",
age: 30
};
// ✅ 正确:提供可选属性
const user2: User = {
name: "John",
age: 30,
email: "john@example.com"
};
// ✅ 正确:使用索引签名允许额外属性
type FlexibleUser = {
name: string;
age: number;
[key: string]: string | number;
};
const user3: FlexibleUser = {
name: "John",
age: 30,
email: "john@example.com", // 通过索引签名允许
score: 95
};注意事项
提示
- 对象类型是结构化的(Structural Typing),只要对象的结构匹配类型定义,就可以赋值,不需要显式声明类型
- 使用可选属性
?时,在访问属性前应该检查属性是否存在,避免运行时错误 - 只读属性
readonly只在编译时生效,运行时仍然可以修改(需要使用Object.freeze()实现真正的不可变)
注意
- 在严格模式下,对象字面量不能包含未在类型定义中声明的属性(除非使用索引签名)
- 索引签名的类型必须包含所有固定属性的类型,否则会出现类型错误
- 可选属性在访问时可能是
undefined,需要进行空值检查
重要
- 对象类型是 TypeScript 类型系统的基础,理解对象类型对于学习接口、类型别名等高级概念非常重要
- 对象类型检查发生在编译时,不会影响运行时的性能
- 使用对象类型可以大大提高代码的可维护性和可读性
相关链接
- 基础类型 - 了解 TypeScript 的基础类型
- 接口 - 学习接口定义和使用
- 类型别名 - 了解类型别名的用法
- 数组和元组 - 学习数组和元组类型
- TypeScript 官方文档 - 对象类型