avatar
Published on

TypeScript type 和 interface区别

TypeScript type 和 interface区别

TypeScript中type和interface的区别

基本定义

interface

用于定义对象的结构,描述对象应该具有的属性和方法。

interface User {
  name: string;
  age: number;
  email?: string; // 可选属性
}

type

是类型别名,可以为任何类型创建一个新的名称。

type User = {
  name: string;
  age: number;
  email?: string; // 可选属性
}

主要区别

1. 扩展性

interface支持扩展,可以通过继承来扩展其他接口:

interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}

// 也可以继承多个接口
interface Cat extends Animal {
  color: string;
}

interface Robot {
  model: string;
}

interface RoboDog extends Dog, Robot {
  batteryLevel: number;
}

type可以通过交叉类型来实现类似的效果:

type Animal = {
  name: string;
}

type Dog = Animal & {
  breed: string;
}

// 也可以组合多个类型
type Robot = {
  model: string;
}

type RoboDog = Dog & Robot & {
  batteryLevel: number;
}

2. 声明合并

interface支持声明合并,即可以多次定义同一个接口,TypeScript会自动合并它们:

interface Window {
  title: string;
}

interface Window {
  ts: TypeScriptAPI;
}

// 最终Window接口包含title和ts两个属性

type不支持声明合并,重复定义会报错:

type Window = {
  title: string;
}

// 错误:标识符“Window”重复
type Window = {
  ts: TypeScriptAPI;
}

3. 类实现

类可以实现interface

interface Point {
  x: number;
  y: number;
}

class MyPoint implements Point {
  x: number;
  y: number;
  
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

类也可以实现type(只要义的是对象类型):

type Point = {
  x: number;
  y: number;
}

class MyPoint implements Point {
  x: number;
  y: number;
  
  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}

4. 类型组合能力

type更灵活,可以用于定义各种类型,包括原始类型、联合类型、元组等:

// 基本类型别名
type Name = string;

// 联合类型
type Status = "pending" | "fulfilled" | "rejected";

// 元组
type Point = [number, number];

// 函数类型
type EventHandler = (event: Event) => void;

// 类型映射
type PartialUser = Partial<User>;

interface主要用于定义对象结构:

// 错误:interface不能用于定义基本类型
// interface Name = string; 

// interface不能直接定义联合类型
// interface Status = "pending" | "fulfilled" | "rejected";

5. 循环引用

interface支持循环引用:

interface TreeNode {
  value: string;
  left?: TreeNode;
  right?: TreeNode;
}

type在某些情况下可能不支持循环引用:

// 可能会报错
type TreeNode = {
  value: string;
  left?: TreeNode;
  right?: TreeNode;
}

使用场景建议

优先使用interface的情况

  1. 定义对象结构:当你需要定义一个对象应该具有哪些属性和方法时
  2. 需要扩展性:当你需要继承其他类型定义时
  3. 需要声明合并:当你需要在多个地方定义同一接口的不同部分时
  4. 定义库的公共API:因为interface支持声明合并,更适合库的扩展
// 定义用户对象结构
interface User {
  id: number;
  name: string;
  email: string;
}

// 扩展用户接口
interface AdminUser extends User {
  permissions: string[];
}

优先使用type的情况

  1. 定义联合类型:当你需要定义多个可能的类型时
  2. 定义元组类型:当你需要定义固定长度和类型的数组时
  3. 定义函数类型:当你需要为函数定义类型时
  4. 定义复杂类型别名:当你需要为复杂类型创建简短的别名时
// 联合类型
type Direction = "up" | "down" | "left" | "right";

// 元组类型
type Coordinate = [number, number];

// 函数类型
type EventHandler = (event: Event) => void;

// 复杂类型别名
type ApiResponse<T> = {
  data: T;
  status: number;
  message: string;
  timestamp: Date;
}

实际应用示例

React组件Props定义

在React中,通常使用interface来定义组件的props:

interface ButtonProps {
  children: React.ReactNode;
  variant?: "primary" | "secondary" | "danger";
  size?: "small" | "medium" | "large";
  onClick?: () => void;
}

const Button: React.FC<ButtonProps> = ({ 
  children, 
  variant = "primary", 
  size = "medium", 
  onClick 
}) => {
  // 组件实现
}

Redux状态类型定义

在Redux中,通常使用type来定义action类型和状态:

// Action类型
type ADD_TODO = {
  type: "ADD_TODO";
  payload: {
    id: number;
    text: string;
  };
};

type REMOVE_TODO = {
  type: "REMOVE_TODO";
  payload: {
    id: number;
  };
};

type TodoAction = ADD_TODO | REMOVE_TODO;

// State类型
type TodoState = {
  todos: Array<{
    id: number;
    text: string;
    completed: boolean;
  }>;
};

总结

特性interfacetype
对象结构定义
扩展性通过extends通过&
声明合并
类实现
联合类型
元组类型
基本类型别名
循环引用有限支持

在实际开发中,可以根据具体需求选择使用interface还是type:

  • 对于对象结构定义,两者基本等价,但interface更符合面向对象的思想
  • 对于复杂类型操作,type更灵活
  • 对于库的公共API,interface更适合,因为它支持声明合并
  • 对于应用内部的类型定义,可以根据团队习惯和个人偏好选择

总的来说,interface更适合定义对象的"形状",而type更适合创建类型的"别名"。