第 1 节 概述
前几篇我们学了基础类型和数组。这就像学会了描述单个数字和列表。
但程序的数据往往是结构化的——一个"人"有姓名、年龄、地址;一个"订单"有编号、商品、金额。
TypeScript 提供了两种方式描述对象的结构:interface 和 type。
第 2 节 对象类型
最直接的方式是用 type 描述对象的形状:
1
2
3
4
5
6
7
8
9
10
11
| type Person = {
name: string
age: number
email: string
}
const user: Person = {
name: "张三",
age: 18,
email: "zhangsan@example.com",
}
|
2.1 可选属性
用 ? 标记属性可选:
1
2
3
4
5
6
7
| type Person = {
name: string
age?: number // 年龄不是必填的
}
const user1: Person = { name: "张三", age: 18 } // ✅
const user2: Person = { name: "李四" } // ✅ age 可选
|
2.2 只读属性
用 readonly 标记属性不可修改:
1
2
3
4
5
6
7
8
| type Config = {
readonly url: string
timeout: number
}
const config: Config = { url: "https://api.com", timeout: 5000 }
config.url = "https://new.com" // ❌ 只读,不能修改
config.timeout = 3000 // ✅ 非只读,可以改
|
第 3 节 interface
interface 是描述对象类型的主要方式。和 type 很像,但有一些独特的能力:
1
2
3
4
5
6
7
8
9
10
11
| interface Person {
name: string
age: number
email: string
}
const user: Person = {
name: "张三",
age: 18,
email: "zhangsan@example.com",
}
|
3.1 声明合并
同名的 interface 会自动合并:
1
2
3
4
5
6
7
8
9
10
11
12
13
| interface Person {
name: string
}
interface Person {
age: number
}
// 等价于
interface Person {
name: string
age: number
}
|
这个特性对扩展第三方库的类型很有用。type 不支持声明合并,重名会报错。
3.2 继承
interface 可以用 extends 继承另一个接口:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| interface Animal {
name: string
eat(): void
}
interface Dog extends Animal {
breed: string // 品种
}
const myDog: Dog = {
name: "旺财",
breed: "金毛",
eat() {
console.log("旺财在吃饭")
},
}
|
可以同时继承多个接口:
1
2
3
4
5
6
7
8
9
10
11
| interface Flyable {
fly(): void
}
interface Swimmable {
swim(): void
}
interface Duck extends Flyable, Swimmable {
quack(): void
}
|
第 4 节 type vs interface
两者大部分功能重叠,但有几处关键区别:
| 对比 | type | interface |
|---|
| 声明合并 | ❌ 不支持 | ✅ 同名自动合并 |
| 继承 | & 交叉类型 | extends 继承 |
| 联合类型 | ✅ type A = B | C | ❌ 不支持 |
| 基本类型别名 | ✅ type Name = string | ❌ 不支持 |
| 类实现 | ✅ 支持 implements | ✅ 支持 implements |
什么时候用哪个?
一个实用的准则是:
- 需要描述对象/类的形状→ 用
interface - 需要联合类型、交叉类型、基本类型别名→ 用
type - 公开 API / 库的类型定义→ 用
interface(方便消费者扩展) - 内部工具类型、组件 props→ 都可以,保持一致即可
1
2
3
4
5
6
7
8
9
10
11
12
13
| // interface 适用于:对象结构、类的契约
interface User {
id: number
name: string
}
// type 适用于:联合类型、工具类型
type Status = "active" | "inactive" | "banned"
type Nullable<T> = T | null
type ApiResponse<T> = {
data: T
status: Status
}
|
第 5 节 接口描述函数
interface 也可以描述函数的形状——调用签名:
1
2
3
4
5
6
7
| interface SearchFunc {
(source: string, subString: string): boolean
}
const mySearch: SearchFunc = function(source, subString) {
return source.includes(subString)
}
|
还有带属性的函数——既是函数,又有附加属性:
1
2
3
4
5
6
7
8
9
10
11
12
| interface Counter {
(start: number): string
interval: number
reset(): void
}
function getCounter(): Counter {
const counter = ((start: number) => "计数") as Counter
counter.interval = 1000
counter.reset = () => {}
return counter
}
|
说明
带属性函数的场景不多见,但在设计库的 API 时会用到。比如 jQuery 的 $ 既是函数,又有 $.ajax 等方法。
第 6 节 索引签名
当对象的属性名不确定时,可以用索引签名描述:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| interface StringArray {
[index: number]: string
}
const myArray: StringArray = ["a", "b", "c"]
interface Dictionary {
[key: string]: unknown
}
const dict: Dictionary = {
"foo": 123,
"bar": "hello",
}
|
索引签名常用于"字典"或"映射"场景:
1
2
3
4
5
6
7
8
9
10
11
| interface UserMap {
[userId: string]: {
name: string
age: number
}
}
const users: UserMap = {
"u001": { name: "张三", age: 18 },
"u002": { name: "李四", age: 20 },
}
|
有了对象和接口,你已经能描述复杂的数据结构了。
但程序不只有数据——还有行为。 函数是行为的基本单位。下一篇看怎么给函数加上类型。