interface 与 type

2026-09-09 00:00    #typescript  

两者都能描述对象,但能力并不等价。选择标准不是"哪个更好",而是"你要用哪个能力"。

能力对比

 1// interface 与 type 的能力对比
 2
 3// ---- 都能做的 ----
 4interface I1 { a: number }
 5type T1 = { a: number };
 6
 7// 扩展
 8interface I2 extends I1 { b: string }
 9type T2 = T1 & { b: string };
10
11// 函数类型
12interface Fn1 { (x: number): string }
13type Fn2 = (x: number) => string;
14
15// 泛型
16interface G1<T> { value: T }
17type G2<T> = { value: T };
18
19// 索引签名
20interface Dict1 { [k: string]: number }
21type Dict2 = { [k: string]: number };
22
23// ---- 只有 type 能做的 ----
24
25// 1. 联合类型
26type Status = "a" | "b";
27// interface Status2 = "a" | "b";   // 语法错误
28
29// 2. 元组类型
30type Pair = [number, number];
31// interface Pair2 extends [number, number] {}  // 不能直接定义元组
32
33// 3. 条件类型
34type IsString<T> = T extends string ? true : false;
35
36// 4. 映射类型
37type Partial2<T> = { [K in keyof T]?: T[K] };
38
39// 5. 原始类型别名
40type ID = string | number;
41type Name = string;
42
43// 6. 模板字面量类型
44type Event = `on${string}`;
45
46// ---- 只有 interface 能做的 ----
47
48// 1. 声明合并
49interface Merged { a: number }
50interface Merged { b: string }
51const merged: Merged = { a: 1, b: "x" };
52
53// type 不能合并
54type TM = { a: number };
55// type TM = { b: string };   // error: Duplicate identifier
56
57// 2. extends 继承(比交叉更严格地检查)
58interface Base { a: number }
59interface Derived extends Base { b: string }
60
61// extends 可以继承多个
62interface Multi extends Base, Merged { c: boolean }
63
64// 3. 类可以实现多个 interface,但 implements 一个 type 联合不行
65interface A1 { a: number }
66interface B1 { b: number }
67class Impl implements A1, B1 {
68  a = 1;
69  b = 2;
70}
71
72// ---- extends 与 & 的一个关键差异 ----
73
74// interface extends:不能覆盖不兼容的属性
75// @ts-expect-error TS2430: Interface 'Bad' incorrectly extends interface 'Base'.
76interface Bad extends Base {
77  a: string;
78}
79
80// type 交叉:不报错,但结果是 never
81type BadType = Base & { a: string };
82// a 的类型变成 number & string = never
83const bad: BadType = { a: 1 as never };
84
85console.log(merged, new Impl(), bad);
1{ a: 1, b: 'x' } Impl { a: 1, b: 2 } { a: 1 }
能力interfacetype
描述对象结构✓✓
函数类型✓✓
泛型✓✓
索引签名✓✓
扩展extends& 交叉
继承多个✓✓(交叉)
声明合并✓✗
联合类型✗✓
元组类型✗✓
条件类型✗✓
映射类型✗✓
原始类型别名✗✓
模板字面量类型✗✓

只有 type 能做的

1type Status = "a" | "b";                          // 联合
2type Pair = [number, number];                     // 元组
3type IsString<T> = T extends string ? true : false;  // 条件类型
4type Partial2<T> = { [K in keyof T]?: T[K] };     // 映射类型
5type ID = string | number;                        // 原始类型别名
6type Event = `on${string}`;                       // 模板字面量

interface 写不出这些,是语法层面的限制(interface X = "a" | "b" 是语法错误)。

只有 interface 能做的

声明合并是 interface 独有的:

1interface Merged { a: number }
2interface Merged { b: string }      // 自动合并
3
4const merged: Merged = { a: 1, b: "x" };

type 会报 Duplicate identifier:

1type TM = { a: number };
2type TM = { b: string };    // error: Duplicate identifier 'TM'.

这是给第三方库补类型的基础(见 声明合并)。需要合并就必须用 interface。

extends 与 & 的关键差异

两者语义上都是"并且",但错误检查行为不同:

 1interface Base { a: number }
 2
 3// interface extends:不兼容的覆盖会报错
 4interface Bad extends Base {
 5  a: string;
 6}
 7// error TS2430: Interface 'Bad' incorrectly extends interface 'Base'.
 8//   Types of property 'a' are incompatible.
 9//     Type 'string' is not assignable to type 'number'.
10
11// type 交叉:不报错,但属性类型变成 never
12type BadType = Base & { a: string };
13// a 的类型是 number & string,即 never
& 不会报错,只会静默产生 never

这是 & 的一个陷阱。number & string 是 never,类型检查器不会提示你写错了,只会在后面赋值时以奇怪的方式失败。interface extends 会立刻报 TS2430,错误更早更清楚。

选择标准

用 interface:

用 type:

实用规则

默认用 interface,因为它可扩展、可合并、错误检查更严格。需要 type 独有的能力时再换 type。

反过来(默认 type,需要合并时换 interface)也行,但会导致团队里风格不统一。关键是定一个默认值并保持一致。

一个常见误区

interface 不是"只能描述对象",它也能描述函数和数组:

1interface Fn { (x: number): string }
2interface Arr extends Array<number> {}

反过来 type 也不是"只能描述联合",它同样能描述对象:

1type Obj = { a: number };

真正的区别只在上面那张能力表里。

相关