详细解读typescript中 interface 和 type 的区别
通过案例详细解读typescript中interface 和type的区别,以及使用场景。
·
在 TypeScript 中,interface
和 type
都用于定义类型,但两者在语法特性、使用场景和扩展能力上有显著差异。接下来我会从五个方面配合案例解读:
1. 声明合并
- Interface:支持声明合并。多次定义同名接口时,TypeScript 会自动合并它们的成员。
interface Person { name: string } interface Person { age: number } // 合并后:{ name: string; age: number }
- Type:不允许重复声明同名类型,会直接报错。
type Person = { name: string }; type Person = { age: number }; // Error: 重复标识符
应用场景:如上例所示,当需要扩展第三方库类型或模块化定义对象结构时,
interface
的声明合并特性非常有用。例如,为现有类型添加自定义属性。
2. 扩展与组合方式
- Interface:使用
extends
关键字扩展其他接口或类型,甚至类。interface Parent { id: number; // 父接口的必选属性 name: string; } interface Child extends Parent { age: number; // 子接口新增的属性 } // 实现子接口的对象必须包含 Parent + Child 所有必选属性 const person: Child = { id: 1, // ✔ 必须实现父接口属性 name: "Alice", // ✔ age: 30 // ✔ 子接口新增属性 };
- Type:通过交叉类型(
&
)组合多个类型,但无法直接扩展类。type Animal = { name: string }; type Dog = Animal & { breed: string };
需要注意:
interface
继承时当子接口通过extends
继承父接口时,子接口的实现必须同时满足父接口和子接口的所有必选属性。且可以重新声明父接口的属性,但新定义的属性类型必须是父接口类型的 子类型或相同类型,否则会导致类型冲突。interface Parent { role: string; } interface Child extends Parent { role: "admin" | "user"; // ✔ 合法:字面量类型是 string 的子类型 } interface Child extends Parent { role: number; // ❌ 错误:number 无法赋值给 string }
type
交叉类型会合并同名属性,可能导致逻辑错误(如string & number
为never
)。interface A { prop: string; } interface B extends A { prop: number; } // Error: 类型冲突 type C = { prop: string; } & { prop: number; }; // prop: never
3. 实现与联合类型
- Type:更擅长定义复杂类型组合,如联合类型(
|
)和元组,// 联合类型(仅 type 支持) type Result = string | number; function print(result: Result) { /* ... */ } // 元组(仅 type 支持) type Coordinates = [number, number];
场景对比:
interface
无法直接定义联合类型,但可通过组合多个接口实现:interface Cat { purrs: boolean; } interface Dog { barks: boolean; } type Pet = Cat | Dog;
4. 类实现(Class Implements)
类可通过 implements
实现 interface
或 type
,但无法实现联合类型的 type
。
interface Person { name: string; }
class Student implements Person { name = "John"; } // 合法
type PersonType = { name: string; };
class Teacher implements PersonType { name = "Jane"; } // 合法
type UnionPerson = { name: string; } | { age: number; };
class Employee implements UnionPerson { /* Error: 无法实现联合类型 */ }
这里怕读者不知道什么是implements 关键字,简单的解释一下:
interface 接口或 type 别名,可以用对象的形式,为 class 指定一组检查条件。然后,类使用 implements 关键字,表示当前类满足这些外部类型条件的限制。
implements
的作用
1. 强制类型检查
- 编译器验证:TypeScript 会在编译时检查类是否完整实现了接口的所有 必选属性/方法(包括类型一致性)。
- 错误提前暴露:若类与接口的成员不匹配(缺少属性、类型不符),编译阶段立即报错,避免运行时潜在问题。
2. 代码约定与文档化
- 明确契约:通过接口定义类必须具备的能力,例如
Country
必须包含name
和capital
。 - 提高可读性:开发者一眼可知类的核心功能,例如
MyCountry
需遵循Country
的规范。
interface Country {
name:string;
capital:string;
}
// 或者
type Country = {
name:string;
capital:string;
}
class MyCountry implements Country {
name = '';
capital = '';
}
上面示例中,interface
或type
都可以定义一个对象类型。类MyCountry
使用implements
关键字,表示该类的实例对象满足这个外部类型。
5. 支持的类型范围
- Interface:主要用于定义对象结构,支持可选属性、只读属性和函数类型,但无法直接定义原始类型、元组或联合类型。
interface User { name: string; age?: number; // 可选属性 readonly id: string; // 只读属性 }
- Type:更灵活,可定义原始类型、元组、联合类型、映射类型等。
type ID = string | number; // 联合类型 type Point = [number, number]; // 元组 type Primitive = string; // 原始类型别名
type
在定义函数类型和工具类型时更灵活。// 函数类型(两种写法) interface AddFunc { (a: number, b: number): number; } type AddType = (a: number, b: number) => number; // 条件类型(仅 type 支持) type IsString<T> = T extends string ? true : false;
场景建议:
- 使用
type
定义函数重载或复杂泛型逻辑。 - 使用
interface
定义对象方法的结构(如类的方法签名)。
总结,概括整篇文章 ----- 何时选择 interface 或 type?
场景 | 推荐使用 | 原因 | 示例 |
---|---|---|---|
定义对象结构并需要扩展 | interface |
支持声明合并和直观的继承逻辑 |
公共 API、第三方库类型扩展 |
联合类型、元组、工具类型 | type |
支持复杂类型操作和组合 |
联合类型、条件类型 |
类实现或方法签名 | interface |
与类继承机制更契合 | 类方法契约 |
函数类型或类型操作 | type |
语法简洁,支持高级类型操作 | 函数重载、泛型工具 |
- 优先
interface
:面向对象设计、需要动态扩展或团队协作时。 - 优先
type
:处理复杂类型逻辑、联合/交叉类型或工具类型时。 - 混合使用:例如用
interface
定义对象结构,用type
定义工具类型 .
更多推荐
所有评论(0)