Skip to content

对象类型

对象是 JavaScript 中最重要的数据结构。TypeScript 提供了多种方式来描述对象的类型,确保对象属性的类型安全。

对象类型基础#

内联类型注解#

最直接的方式是在变量声明时内联定义对象类型:

// TypeScript 5.x
// 内联对象类型
const user: { name: string; age: number } = {
name: '张三',
age: 25,
}
// 函数参数使用对象类型
function printUser(user: { name: string; age: number }) {
console.log(`${user.name}, ${user.age}`)
}
// 函数返回对象类型
function createUser(): { id: number; name: string } {
return { id: 1, name: '新用户' }
}

类型推断#

TypeScript 会自动推断对象字面量的类型:

// 自动推断为 { name: string; age: number; active: boolean }
const person = {
name: '张三',
age: 25,
active: true,
}
// 推断的类型可以用于后续操作
person.name = '李四' // ✅ 正确
// person.email = 'test@test.com'; // ❌ 属性不存在

可选属性#

使用 ? 标记可选属性:

// email 和 phone 是可选的
const user: {
name: string
age: number
email?: string
phone?: string
} = {
name: '张三',
age: 25,
// email 和 phone 可以不提供
}
// 可选属性的类型是 T | undefined
function greet(user: { name: string; title?: string }) {
if (user.title) {
console.log(`${user.title} ${user.name}`)
} else {
console.log(user.name)
}
}
greet({ name: '张三' }) // 张三
greet({ name: '张三', title: '博士' }) // 博士 张三

🔶 可选属性的类型实际上是 T | undefined,访问前需要检查:

function getEmailLength(user: { email?: string }): number {
// return user.email.length; // ❌ 错误:email 可能是 undefined
// 方式1:条件检查
if (user.email) {
return user.email.length
}
return 0
// 方式2:可选链
return user.email?.length ?? 0
// 方式3:非空断言(确定不为空时)
return user.email!.length // 谨慎使用
}

只读属性#

使用 readonly 标记只读属性:

const config: {
readonly apiUrl: string
readonly timeout: number
retries: number // 可修改
} = {
apiUrl: 'https://api.example.com',
timeout: 5000,
retries: 3,
}
// config.apiUrl = 'xxx'; // ❌ 错误:只读属性
config.retries = 5 // ✅ 正确

🤔 readonly 是浅层的:

const user: {
readonly info: { name: string; age: number }
} = {
info: { name: '张三', age: 25 },
}
// user.info = { name: '李四', age: 30 }; // ❌ 不能重新赋值
user.info.name = '李四' // ✅ 可以修改嵌套属性!

索引签名#

当对象有动态的属性名时,使用索引签名:

// 字符串索引签名
const scores: { [key: string]: number } = {
math: 90,
english: 85,
physics: 88,
}
scores['chemistry'] = 92 // ✅ 可以添加新属性
console.log(scores['math']) // 90
// 数字索引签名(类似数组)
const items: { [index: number]: string } = {
0: 'first',
1: 'second',
2: 'third',
}

索引签名与固定属性#

// 索引签名可以和固定属性混用
interface Dictionary {
length: number // 固定属性
[key: string]: number // 索引签名
}
const dict: Dictionary = {
length: 3,
a: 1,
b: 2,
c: 3,
}
// 🔶 注意:固定属性的类型必须兼容索引签名的类型
interface InvalidDict {
name: string // ❌ 错误:string 不能赋给 number
[key: string]: number
}
// ✅ 正确:使用联合类型
interface ValidDict {
name: string
[key: string]: string | number
}

Record 工具类型#

Record<K, V> 是更简洁的索引签名语法:

// 等价于 { [key: string]: number }
const scores: Record<string, number> = {
math: 90,
english: 85,
}
// 限定键的类型
type Subject = 'math' | 'english' | 'physics'
const grades: Record<Subject, number> = {
math: 90,
english: 85,
physics: 88,
}

嵌套对象#

// 嵌套对象类型
const company: {
name: string
address: {
city: string
street: string
zipCode: string
}
employees: {
name: string
role: string
}[]
} = {
name: 'Acme Inc',
address: {
city: '北京',
street: '中关村大街1号',
zipCode: '100000',
},
employees: [
{ name: '张三', role: '工程师' },
{ name: '李四', role: '设计师' },
],
}

🎯 对于复杂的嵌套结构,建议拆分成多个类型:

// 拆分类型定义(更清晰)
type Address = {
city: string
street: string
zipCode: string
}
type Employee = {
name: string
role: string
}
type Company = {
name: string
address: Address
employees: Employee[]
}
const company: Company = {
name: 'Acme Inc',
address: {
city: '北京',
street: '中关村大街1号',
zipCode: '100000',
},
employees: [
{ name: '张三', role: '工程师' },
{ name: '李四', role: '设计师' },
],
}

对象类型的结构化类型#

TypeScript 使用结构化类型系统(鸭子类型):只要结构匹配,类型就兼容。

type Point = {
x: number
y: number
}
// 对象可以有额外属性
const point3D = { x: 1, y: 2, z: 3 }
function printPoint(p: Point) {
console.log(`(${p.x}, ${p.y})`)
}
// ✅ 正确:point3D 有 x 和 y 属性
printPoint(point3D)

🔶 但对象字面量会有额外属性检查:

type Point = { x: number; y: number }
// ❌ 对象字面量直接赋值时会检查额外属性
const p: Point = { x: 1, y: 2, z: 3 } // 错误:z 不在类型中
// ✅ 通过变量赋值可以绕过检查
const temp = { x: 1, y: 2, z: 3 }
const p2: Point = temp // 正确
// ✅ 或使用类型断言
const p3 = { x: 1, y: 2, z: 3 } as Point

空对象类型#

// 空对象类型 {} 表示"非 null 和 undefined 的任意值"
let obj: {} = { name: '张三' }
obj = 'hello' // ✅ 字符串也行
obj = 123 // ✅ 数字也行
obj = [] // ✅ 数组也行
// obj = null; // ❌ 错误
// obj = undefined; // ❌ 错误
// 真正的空对象应该用其他方式表示
type EmptyObject = Record<string, never>
// 或
type EmptyObject2 = { [key: string]: never }

object 类型#

object 类型表示非原始类型的值:

// object:非原始类型(不是 string、number、boolean 等)
let obj: object
obj = { name: '张三' } // ✅
obj = [1, 2, 3] // ✅
obj = () => {} // ✅
// obj = 'hello'; // ❌ 原始类型
// obj = 123; // ❌ 原始类型
// obj = true; // ❌ 原始类型
// 🔶 object 类型无法访问具体属性
const o: object = { name: '张三' }
// console.log(o.name); // ❌ 错误

🤔 object vs Object vs {}

类型含义可接受的值
object非原始类型对象、数组、函数
Object所有有 Object 方法的值几乎所有值(不推荐使用)
{}非 null/undefined 的任意值几乎所有值

实际应用#

API 响应类型#

// API 响应的通用结构
type ApiResponse<T> = {
code: number
message: string
data: T
timestamp: number
}
// 用户数据
type User = {
id: number
name: string
email: string
createdAt: string
}
// 使用
const response: ApiResponse<User> = {
code: 200,
message: 'success',
data: {
id: 1,
name: '张三',
email: 'zhangsan@example.com',
createdAt: '2024-01-01',
},
timestamp: Date.now(),
}

配置对象#

type DatabaseConfig = {
readonly host: string
readonly port: number
readonly database: string
username?: string
password?: string
options?: {
ssl?: boolean
timeout?: number
}
}
const dbConfig: DatabaseConfig = {
host: 'localhost',
port: 5432,
database: 'myapp',
options: {
ssl: true,
},
}

表单数据#

type FormData = {
[field: string]: string | number | boolean | undefined
name: string
email: string
age?: number
newsletter?: boolean
}
function validateForm(data: FormData): boolean {
return data.name.length > 0 && data.email.includes('@')
}

常见问题#

🙋 什么时候用 type,什么时候用 interface?#

两者在定义对象类型时基本等价。一般建议:

// 两者效果相同
interface User1 {
name: string
age: number
}
type User2 = {
name: string
age: number
}

🙋 如何让所有属性变成可选?#

使用 Partial<T> 工具类型:

type User = {
name: string
age: number
email: string
}
type PartialUser = Partial<User>
// 等价于 { name?: string; age?: number; email?: string }
const update: PartialUser = { name: '新名字' }

🙋 如何让所有属性变成只读?#

使用 Readonly<T> 工具类型:

type User = {
name: string
age: number
}
type ReadonlyUser = Readonly<User>
// 等价于 { readonly name: string; readonly age: number }
const user: ReadonlyUser = { name: '张三', age: 25 }
// user.name = '李四'; // ❌ 错误

总结#

特性语法用途
对象类型{ prop: type }描述对象结构
可选属性prop?: type属性可以不存在
只读属性readonly prop: type属性不可修改
索引签名{ [key: string]: type }动态属性名
RecordRecord<K, V>简洁的索引类型

下一篇我们将学习函数类型,了解如何为函数添加完整的类型注解。