在 TypeScript 中,interface
和 type
都用于定义类型,但它们在设计目的、语法和功能上有明显差异。以下是它们的核心区别:
1. 基本概念
-
**
interface
**专门用于定义 对象类型 的形状(Object Shape),强调结构描述,适合描述类、对象、函数等结构。
typescripttypescript interface User { name: string; age: number; }
-
**
type
**类型别名,可为任何类型(包括原始类型、联合类型、元组等)命名,用途更广泛。
typescripttypescript type ID = string | number; // 联合类型 type Point = [number, number]; // 元组 type Callback = () => void; // 函数类型
2. 声明合并(Declaration Merging)
-
**
interface
**支持同名声明自动合并,适合扩展第三方类型或逐步定义类型。
kotlintypescript interface User { name: string; } interface User { age: number; } // 合并为 { name: string; age: number; }
-
**
type
**同名重复声明会报错,无法合并。
initypescript type User = { name: string }; // ❌ Error: Duplicate identifier type User = { age: number };
3. 继承与扩展
-
**
interface
**使用
extends
继承其他接口,语法更直观。phptypescript interface Animal { name: string; } interface Dog extends Animal { bark(): void; }
-
**
type
**使用交叉类型
&
实现类似继承的效果,更灵活。initypescript type Animal = { name: string }; type Dog = Animal & { bark(): void };
4. 适用场景
-
**
interface
专长**- 定义对象结构(如类、函数、对象)。
- 需要声明合并(如扩展第三方类型)。
- 类实现接口(
implements
)。
-
**
type
专长**- 定义联合类型、元组、映射类型等复杂类型。
- 使用条件类型(
extends ? :
)、模板字面量类型等高级特性。 - 为其他类型提供别名(包括原始类型)。
initypescript type Status = "success" | "error"; // 字面量联合类型 type Keys = keyof User; // 映射类型
5. 其他区别
特性 | interface |
type |
---|---|---|
函数类型定义 | 支持对象语法和函数重载 | 只能直接定义函数签名 |
动态属性 | 无法直接使用动态计算属性 | 可使用映射类型(in )动态生成属性 |
类实现 | 支持 implements |
若为对象类型,也能被类实现 |
扩展性 | 更适合通过声明合并逐步扩展 | 扩展需重新定义或使用交叉类型 |
映射类型(Mapped Types)
在 TypeScript 中,映射类型(Mapped Types) 是一种基于现有类型动态生成新类型的高级特性。它可以通过 in
关键字遍历一个联合类型的键集合,并为每个键生成新的属性类型。这是 type
独有的能力,interface
无法直接实现类似操作。
核心概念:用 in
动态生成属性
映射类型的基本语法为:
ini
typescript
type NewType = {
[Key in Keys]: ValueType;
};
- **
Key
**:遍历的键(变量名,可以是任意标识符,如K
)。 - **
Keys
**:一个联合类型(例如"name" | "age"
),表示要遍历的键集合。 - **
ValueType
**:每个键对应的值的类型(可以是固定类型,或基于原类型的动态类型)。
具体示例
1. 基本动态属性生成
假设有一个联合类型 Keys
,基于它生成一个对象类型,每个属性的值类型为 string
:
ini
typescript
type Keys = "name" | "age" | "email";
type DynamicObject = {
[K in Keys]: string; // 遍历 Keys 中的每个键,值为 string
};
// 等价于:
type DynamicObject = {
name: string;
age: string;
email: string;
};
2. 基于现有类型转换
映射类型常与 keyof
结合,动态操作现有类型的所有属性:
ini
typescript
interface User {
name: string;
age: number;
email: string;
}
// 将 User 的所有属性转换为可选属性
type OptionalUser = {
[K in keyof User]?: User[K];
};
// 等价于:
type OptionalUser = {
name?: string;
age?: number;
email?: string;
};
3. 更复杂的类型操作
可以结合条件类型、模板字面量等特性,实现更动态的转换:
typescript
typescript
// 将 User 的所有属性名改为 `getXxx` 格式,且值为函数
type Getters = {
[K in keyof User as `get${Capitalize<K>}`]: () => User[K];
};
// 等价于:
type Getters = {
getName: () => string;
getAge: () => number;
getEmail: () => string;
};
为什么 interface
无法实现?
interface
的设计初衷是声明对象的结构 ,而不是动态生成类型。它无法直接遍历联合类型或使用 in
动态生成属性。例如:
csharp
typescript
// ❌ 错误:interface 不支持映射类型语法
interface DynamicInterface {
[K in "name" | "age"]: string;
}
实际应用场景
-
属性修饰符操作
快速生成
Readonly
、Partial
、Required
等工具类型:initypescript type ReadonlyUser = Readonly<User>; // 所有属性变为只读 type PartialUser = Partial<User>; // 所有属性变为可选
-
类型裁剪或转换
修改属性名或值的类型:
initypescript // 将 User 的所有属性值改为 boolean type BooleanUser = { [K in keyof User]: boolean; };
-
筛选特定属性
结合条件类型过滤属性:
initypescript // 只保留 User 中的字符串类型属性 type StringProps = { [K in keyof User as User[K] extends string ? K : never]: User[K]; }; // 等价于 { name: string; email: string }
总结
- **
type
+ 映射类型**:通过in
动态遍历联合类型的键集合,生成新的属性,适合复杂类型操作。 - **
interface
**:静态定义对象结构,无法动态生成属性。
当需要基于现有类型动态生成或修改属性时,映射类型是 type
的杀手级特性,而 interface
无法替代这一能力。
6. 错误提示
- **
interface
**
错误提示更清晰,直接指出缺少或多余的属性。 - **
type
**
复杂类型(如联合类型)的错误提示可能较冗长。
7. 如何选择?
- 默认使用
interface
:定义对象结构、需要声明合并时。 - 使用
type
:需要联合类型、元组、高级类型操作时。
两者在多数场景下可以互换,但理解差异能帮助写出更清晰的类型代码。
8. 总结
对比维度 | **interface ** |
**type ** |
---|---|---|
基本用途 | 定义对象类型(类、函数、对象的结构) | 为任何类型命名(包括原始类型、联合类型、元组、函数等) |
声明合并 | ✅ 支持同名合并(多次声明自动合并) | ❌ 禁止同名重复声明 |
继承语法 | extends 关键字(直观继承接口) |
交叉类型 & (通过 & 合并类型) |
动态属性生成 | ❌ 不支持映射类型(无法用 in 动态生成属性) |
✅ 支持映射类型(通过 in 遍历键集合生成属性) |
适用场景 | 对象结构定义、声明合并、类实现(implements ) |
联合类型、元组、条件类型、模板字面量类型、复杂类型操作 |
函数类型定义 | 支持对象语法和函数重载: { (x: number): number } |
只能直接定义函数签名: type Fn = (x: number) => number |
工具类型支持 | 需结合 extends 或工具类型间接操作 |
直接支持 Partial 、Readonly 、Pick 等工具类型 |
扩展性 | 适合逐步扩展(通过声明合并) | 需重新定义或交叉类型扩展 |
错误提示 | 更清晰(直接指出属性缺失或冗余) | 复杂类型(如联合类型)可能提示冗长 |
类实现 | 直接通过 implements 实现 |
若为对象类型,也能被类实现 |
示例 | interface User {<br> name: string;<br>} |
type User = {<br> name: string;<br>} |
映射类型示例 | ❌ 不可用 | ✅ type Optional<T> = {<br> [K in keyof T]?: T[K];<br>} |