[Medium] Interface vs Type Alias
1. What are Interface and Type Alias?
什么是 Interface 和 Type Alias?
Interface(接口)
定义:用于定义对象的结构,描述对象应该有哪些属性和方法。
interface User {
name: string;
age: number;
email?: string; // 可选属性
}
const user: User = {
name: 'John',
age: 30,
};
Type Alias(类型别名)
定义:为类型创建一个别名,可以用于任何类型,不仅限于对象。
type User = {
name: string;
age: number;
email?: string;
};
const user: User = {
name: 'John',
age: 30,
};
2. Interface vs Type Alias: Key Differences
Interface 与 Type Alias 的主要差异
1. 扩展方式
Interface:使用 extends
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
const dog: Dog = {
name: 'Buddy',
breed: 'Golden Retriever',
};
Type Alias:使用交叉类型(Intersection)
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
const dog: Dog = {
name: 'Buddy',
breed: 'Golden Retriever',
};
2. 合并(Declaration Merging)
Interface:支持合并
interface User {
name: string;
}
interface User {
age: number;
}
// 自动合并为 { name: string; age: number; }
const user: User = {
name: 'John',
age: 30,
};
Type Alias:不支持合并
type User = {
name: string;
};
type User = { // ❌ 错误:Duplicate identifier 'User'
age: number;
};
3. 适用范围
Interface:主要用于对象结构
interface User {
name: string;
age: number;
}
Type Alias:可用于任何类型
// 基本类型
type ID = string | number;
// 函数类型
type Greet = (name: string) => string;
// 联合类型
type Status = 'active' | 'inactive' | 'pending';
// 元组
type Point = [number, number];
// 对象
type User = {
name: string;
age: number;
};
4. 计算属性
Interface:不支持计算属性
interface User {
[key: string]: any; // 索引签名
}
Type Alias:支持更复杂的类型运算
type Keys = 'name' | 'age' | 'email';
type User = {
[K in Keys]: string; // 映射类型
};
3. When to Use Interface vs Type Alias?
何时使用 Interface?何时使用 Type Alias?
使用 Interface 的情况
-
定义对象结构(最常见)
interface User {
name: string;
age: number;
} -
需要声明合并
// 扩展第三方包的类型定义
interface Window {
myCustomProperty: string;
} -
定义类(Class)的契约
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
使用 Type Alias 的情况
-
定义联合类型或交叉类型
type ID = string | number;
type Status = 'active' | 'inactive'; -
定义函数类型
type EventHandler = (event: Event) => void; -
定义元组
type Point = [number, number]; -
需要映射类型或条件类型
type Partial<T> = {
[P in keyof T]?: T[P];
};
4. Common Interview Questions
常见面试题目
题目 1:基本差异
请说明以下两种定义方式的差异。
// 方式 1:使用 Interface
interface User {
name: string;
age: number;
}
// 方式 2:使用 Type Alias
type User = {
name: string;
age: number;
};
点击查看答案
相同点:
- 两者都可以用来定义对象结构
- 使用方式完全相同
- 都可以扩展和继承
不同点:
-
声明合并:
// Interface 支持
interface User {
name: string;
}
interface User {
age: number;
}
// 自动合并为 { name: string; age: number; }
// Type Alias 不支持
type User = { name: string; };
type User = { age: number; }; // ❌ 错误 -
适用范围:
// Interface 主要用于对象
interface User { ... }
// Type Alias 可用于任何类型
type ID = string | number;
type Handler = (event: Event) => void;
type Point = [number, number];
建议:
- 定义对象结构时,两者都可以使用
- 如果需要声明合并,使用 Interface
- 如果需要定义非对象类型,使用 Type Alias
题目 2:扩展方式
请说明以下两种扩展方式的差异。
// 方式 1:Interface extends
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// 方式 2:Type intersection
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
点击查看答案
Interface extends:
interface Animal {
name: string;
}
interface Dog extends Animal {
breed: string;
}
// 等价于
interface Dog {
name: string;
breed: string;
}
Type intersection:
type Animal = {
name: string;
};
type Dog = Animal & {
breed: string;
};
// 等价于
type Dog = {
name: string;
breed: string;
};
差异:
- 语法:Interface 使用
extends,Type 使用& - 结果:两者结果相同
- 可读性:Interface 的
extends更直观 - 灵活性:Type 的
&可以组合多个类型
示例:
// Interface:只能 extends 一个
interface Dog extends Animal {
breed: string;
}
// Type:可以组合多个
type Dog = Animal & Canine & {
breed: string;
};
题目 3:声明合并
请说明以下代码的行为。
interface User {
name: string;
}
interface User {
age: number;
}
const user: User = {
name: 'John',
// 缺少 age 会如何?
};
点击查看答案
interface User {
name: string;
}
interface User {
age: number;
}
// 两个声明会自动合并为:
// interface User {
// name: string;
// age: number;
// }
const user: User = {
name: 'John',
// ❌ 错误:Property 'age' is missing in type '{ name: string; }' but required in type 'User'
};
正确写法:
const user: User = {
name: 'John',
age: 30, // ✅ 必须包含 age
};
声明合并的应用场景:
// 扩展第三方包的类型定义
interface Window {
myCustomProperty: string;
}
// 现在可以使用
window.myCustomProperty = 'value';
注意:Type Alias 不支持声明合并
type User = { name: string; };
type User = { age: number; }; // ❌ 错误:Duplicate identifier
题目 4:实现(implements)
请说明 Interface 和 Type Alias 在类实现上的差异。
// 情况 1:Interface
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
// 情况 2:Type Alias
type Flyable = {
fly(): void;
};
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
点击查看答案
两者都可以用于 implements:
// Interface
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
// Type Alias(对象类型)
type Flyable = {
fly(): void;
};
class Bird implements Flyable {
fly(): void {
console.log('Flying');
}
}
差异:
- Interface:传统上更常用于定义类的契约
- Type Alias:也可以使用,但语义上 Interface 更合适
建议:
- 定义类契约时,优先使用 Interface
- 如果已经使用 Type Alias 定义,也可以实现
注意:Type Alias 定义的函数类型不能实现
type Flyable = () => void;
class Bird implements Flyable { // ❌ 错误:只能实现对象类型
// ...
}
5. Best Practices
最佳实践
推荐做法
// 1. 定义对象结构时,优先使用 Interface
interface User {
name: string;
age: number;
}
// 2. 定义联合类型时,使用 Type Alias
type Status = 'active' | 'inactive' | 'pending';
type ID = string | number;
// 3. 定义函数类型时,使用 Type Alias
type EventHandler = (event: Event) => void;
// 4. 需要声明合并时,使用 Interface
interface Window {
customProperty: string;
}
// 5. 定义类契约时,使用 Interface
interface Flyable {
fly(): void;
}
class Bird implements Flyable {
fly(): void {}
}
避免的做法
// 1. 不要混用 Interface 和 Type Alias 定义相同的结构
interface User { ... }
type User = { ... }; // ❌ 造成混淆
// 2. 不要用 Type Alias 定义简单的对象结构(除非有特殊需求)
type User = { // ⚠️ Interface 更合适
name: string;
};
// 3. 不要用 Interface 定义非对象类型
interface ID extends string {} // ❌ 不支持
type ID = string | number; // ✅ 正确
6. Interview Summary
面试总结
快速记忆
Interface(接口):
- 主要用于定义对象结构
- 支持声明合并(Declaration Merging)
- 使用
extends扩展 - 适合定义类契约
Type Alias(类型别名):
- 可用于任何类型
- 不支持声明合并
- 使用
&交叉类型扩展 - 适合定义联合类型、函数类型、元组
面试回答范例
Q: Interface 和 Type Alias 的差异是什么?
"Interface 和 Type Alias 都可以用来定义对象结构,但有一些关键差异:1) Interface 支持声明合并,可以多次声明同一个 Interface 并自动合并;Type Alias 不支持。2) Interface 主要用于对象结构;Type Alias 可以用于任何类型,包括联合类型、函数类型、元组等。3) Interface 使用 extends 扩展;Type Alias 使用交叉类型 & 扩展。4) Interface 更适合定义类的契约。一般来说,定义对象结构时两者都可以,但需要声明合并时必须使用 Interface,定义非对象类型时必须使用 Type Alias。"
Q: 什么时候应该使用 Interface?什么时候应该使用 Type Alias?
"使用 Interface 的情况:定义对象结构、需要声明合并(如扩展第三方包类型)、定义类契约。使用 Type Alias 的情况:定义联合类型或交叉类型、定义函数类型、定义元组、需要映射类型或条件类型。简单来说,对象结构优先考虑 Interface,其他类型使用 Type Alias。"