typescript中tpye和interface的区别

TypeScript 中 Type 和 Interface 的区别

概述

在 TypeScript 中,typeinterface 都可以用来定义类型,经常傻傻分不清楚该用哪种,现在就终结这个问题,看看它们的区别和适用场景。

基本语法对比

Interface 语法

typescript 复制代码
interface User {
    id: number;
    name: string;
    email: string;
}

Type 语法

typescript 复制代码
type User = {
    id: number;
    name: string;
    email: string;
};

主要区别

1. 扩展性(Declaration Merging)

Interface 支持声明合并
typescript 复制代码
interface User {
    id: number;
    name: string;
}

// 可以在其他地方扩展同名接口
interface User {
    email: string;
}

// 最终 User 接口包含所有属性
const user: User = {
    id: 1,
    name: 'John',
    email: 'john@example.com'
};
Type 不支持声明合并
typescript 复制代码
type User = {
    id: number;
    name: string;
};

// ❌ 错误:重复的标识符
type User = {
    email: string;
};

2. 继承方式

Interface 使用 extends
typescript 复制代码
interface Animal {
    name: string;
    age: number;
}

interface Dog extends Animal {
    breed: string;
    bark(): void;
}

// 多重继承
interface Pet extends Animal, Dog {
    owner: string;
}
Type 使用交叉类型(&)
typescript 复制代码
type Animal = {
    name: string;
    age: number;
};

type Dog = Animal & {
    breed: string;
    bark(): void;
};

// 多重继承
type Pet = Animal & Dog & {
    owner: string;
};

3. 联合类型支持

Type 可以定义联合类型
typescript 复制代码
// ✅ Type 可以
type Status = 'loading' | 'success' | 'error';
type ID = string | number;
type Response = User | Error;

// 复杂联合类型
type Theme = 'light' | 'dark';
type Size = 'small' | 'medium' | 'large';
type ButtonVariant = `${Theme}-${Size}`;
Interface 不能直接定义联合类型
typescript 复制代码
// ❌ Interface 不能
interface Status = 'loading' | 'success' | 'error'; // 语法错误

4. 原始类型别名

Type 可以为原始类型创建别名
typescript 复制代码
// ✅ Type 可以
type UserID = string;
type Age = number;
type IsActive = boolean;

// 函数类型别名
type EventHandler = (event: Event) => void;
type Predicate<T> = (item: T) => boolean;
Interface 不能为原始类型创建别名
typescript 复制代码
// ❌ Interface 不能
interface UserID = string; // 语法错误

5. 计算属性

Type 支持计算属性
typescript 复制代码
type EventNames = 'click' | 'scroll' | 'mousemove';
type EventHandlers = {
    [K in EventNames as `on${Capitalize<K>}`]: (event: Event) => void;
};
// 结果:{ onClick: ..., onScroll: ..., onMousemove: ... }

// 映射类型
type Partial<T> = {
    [P in keyof T]?: T[P];
};

type UserPartial = Partial<User>;
Interface 不直接支持计算属性
typescript 复制代码
// ❌ Interface 不能直接使用映射类型
interface EventHandlers = {
    [K in EventNames as `on${Capitalize<K>}`]: (event: Event) => void;
}; // 语法错误

具体使用场景

1. 对象形状定义

typescript 复制代码
// 两者都可以,但 interface 更传统
interface ApiResponse {
    data: any;
    status: number;
    message: string;
}

type ApiResponse2 = {
    data: any;
    status: number;
    message: string;
};

2. 函数类型定义

typescript 复制代码
// Type 更简洁
type EventCallback = (event: Event) => void;
type Compare<T> = (a: T, b: T) => number;

// Interface 语法较冗长
interface EventCallback2 {
    (event: Event): void;
}

3. 类的实现

typescript 复制代码
// 两者都可以被类实现
interface Flyable {
    fly(): void;
}

type Swimmable = {
    swim(): void;
};

class Bird implements Flyable, Swimmable {
    fly() { console.log('Flying'); }
    swim() { console.log('Swimming'); }
}

4. 泛型约束

typescript 复制代码
// 两者都可以用作泛型约束
interface HasLength {
    length: number;
}

type HasSize = {
    size: number;
};

function process<T extends HasLength>(item: T): T {
    console.log(item.length);
    return item;
}

高级用法对比

1. 条件类型

typescript 复制代码
// ✅ Type 支持条件类型
type NonNullable<T> = T extends null | undefined ? never : T;
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

// ❌ Interface 不支持
interface NonNullable<T> = T extends null | undefined ? never : T; // 错误

2. 索引签名

typescript 复制代码
// 两者都支持索引签名
interface StringDictionary {
    [key: string]: string;
}

type NumberDictionary = {
    [key: string]: number;
};

// Type 可以更复杂的索引
type KeyOfType<T, U> = {
    [K in keyof T]: T[K] extends U ? K : never;
}[keyof T];

3. 模板字面量类型

typescript 复制代码
// ✅ Type 支持模板字面量
type CSSProperty = `--${string}`;
type EventName<T extends string> = `on${Capitalize<T>}`;

// 复杂的模板类型
type Route = '/users' | '/posts' | '/comments';
type ApiEndpoint<T extends Route> = `https://api.example.com${T}`;

实际项目中的选择建议

1. 优先使用 Interface 的场景

typescript 复制代码
// 1. 定义对象结构,特别是可能需要扩展的
interface User {
    id: string;
    name: string;
    email: string;
}

// 2. 定义类的契约
interface Repository<T> {
    findById(id: string): Promise<T>;
    save(entity: T): Promise<T>;
    delete(id: string): Promise<void>;
}

// 3. React 组件 Props(可能需要扩展)
interface ButtonProps {
    children: React.ReactNode;
    onClick?: () => void;
    disabled?: boolean;
}

// 4. API 响应类型(可能需要扩展)
interface ApiUser {
    id: number;
    username: string;
    email: string;
}

2. 优先使用 Type 的场景

typescript 复制代码
// 1. 联合类型
type Status = 'idle' | 'loading' | 'success' | 'error';
type Theme = 'light' | 'dark' | 'auto';

// 2. 函数类型
type EventHandler<T = Event> = (event: T) => void;
type Predicate<T> = (item: T) => boolean;

// 3. 工具类型和映射类型
type Partial<T> = {
    [P in keyof T]?: T[P];
};

type Pick<T, K extends keyof T> = {
    [P in K]: T[P];
};

// 4. 条件类型
type NonNullable<T> = T extends null | undefined ? never : T;

// 5. 原始类型别名
type ID = string;
type Timestamp = number;

3. 在 React + TypeScript 项目中的实践

typescript 复制代码
// Props 定义:使用 interface(便于扩展)
interface UserCardProps {
    user: User;
    onEdit?: (user: User) => void;
    className?: string;
}

// 状态类型:使用 type(通常是联合类型)
type LoadingState = 'idle' | 'pending' | 'fulfilled' | 'rejected';

// 事件处理器:使用 type
type SubmitHandler = (data: FormData) => void;

// API 类型:根据情况选择
interface ApiResponse<T> {
    data: T;
    status: number;
    message: string;
}

type ErrorResponse = {
    error: string;
    code: number;
};

// Hook 返回类型:使用 type
type UseUserResult = {
    user: User | null;
    loading: boolean;
    error: string | null;
    refetch: () => void;
};

4. Zustand Store 中的应用

typescript 复制代码
// 状态接口:使用 interface(结构化、可扩展)
interface UserState {
    users: User[];
    currentUser: User | null;
    loading: boolean;
    error: string | null;
}

// 动作接口:使用 interface(可能需要扩展)
interface UserActions {
    setUsers: (users: User[]) => void;
    setCurrentUser: (user: User | null) => void;
    setLoading: (loading: boolean) => void;
}

// 组合 Store 类型:使用 type(交叉类型)
type UserStore = UserState & UserActions;

// 工具类型:使用 type
type StoreSlice<T> = (
    set: (fn: (state: T) => void) => void,
    get: () => T
) => T;

性能和编译时间

Interface 的优势

  • 编译器缓存更好
  • 错误信息更清晰
  • 声明合并提供更好的扩展性

Type 的优势

  • 更灵活的类型操作
  • 支持联合类型和计算类型
  • 更适合函数式编程风格

决策流程图

go 复制代码
需要定义类型?
├── 是联合类型? → 使用 type
├── 是函数类型? → 使用 type
├── 是对象结构?
│   ├── 需要声明合并? → 使用 interface
│   ├── 需要映射类型? → 使用 type
│   ├── 是类的契约? → 使用 interface
│   └── 一般情况 → 偏向 interface
└── 是工具类型? → 使用 type

总结

何时使用 Interface

  • ✅ 定义对象结构
  • ✅ 类的契约
  • ✅ 需要声明合并
  • ✅ API 响应类型
  • ✅ React 组件 Props

何时使用 Type

  • ✅ 联合类型
  • ✅ 函数类型
  • ✅ 工具类型
  • ✅ 映射类型
  • ✅ 条件类型
  • ✅ 原始类型别名

一般建议

  1. 默认使用 interface 定义对象结构
  2. 使用 type 定义联合类型和函数类型
  3. 团队保持一致 的编码风格
  4. 根据具体需求 选择最合适的方式

记住:在大多数情况下,两者可以互换使用,选择哪个更多的是代码风格和团队约定的问题。

1. 核心区别总结

特性 Interface Type
声明合并 ✅ 支持 ❌ 不支持
联合类型 ❌ 不支持 ✅ 支持
继承方式 extends &(交叉类型)
原始类型别名 ❌ 不支持 ✅ 支持
条件类型 ❌ 不支持 ✅ 支持
映射类型 ❌ 不支持 ✅ 支持
计算属性 ❌ 不支持 ✅ 支持
编译性能 ✅ 更好 一般
相关推荐
余杭子曰28 分钟前
组件设计模式:聪明组件还是傻瓜组件?
前端
杨超越luckly35 分钟前
HTML应用指南:利用GET请求获取全国小米之家门店位置信息
前端·arcgis·html·数据可视化·shp
海绵宝龙43 分钟前
axios封装对比
开发语言·前端·javascript
Data_Adventure43 分钟前
setDragImage
前端·javascript
南岸月明1 小时前
七月复盘,i人副业自媒体成长笔记:从迷茫到觉醒的真实经历
前端
静水流深LY1 小时前
Vue2学习-el与data的两种写法
前端·vue.js·学习
玲小珑1 小时前
Next.js 教程系列(二十一)核心 Web Vitals 与性能调优
前端·next.js
YGY Webgis糕手之路1 小时前
Cesium 快速入门(八)Primitive(图元)系统深度解析
前端·经验分享·笔记·vue·web
懋学的前端攻城狮1 小时前
从 UI = f(state) 到 Fiber 架构:解构 React 设计哲学的“第一性原理”
前端·react.js·前端框架
三原1 小时前
6年前端学习Java Spring boot 要怎么学?
java·前端·javascript