mcpField
T
type-vs-interface

type-vs-interface

github.com/marius-townhouse开发645 次安装38 周前更新
代码仓库

关于 type-vs-interface

这项 TypeScript 技能说明如何在声明和组合类型时选择 `type` 或 `interface`,帮助开发者处理对象结构、联合类型、元组及可扩展 API。

主要功能

  1. 01按类型形态和用途选择
  2. 02用 interface 定义可扩展对象
  3. 03用别名定义联合类型和元组
  4. 04理解声明合并
  5. 05通过扩展或交叉组合类型

适用场景

  1. 01定义公共 API 的对象结构
  2. 02建模联合类型和元组数据
  3. 03为库类型选择声明方式
  4. 04保持代码库中的类型选择一致

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill type-vs-interface

安装后在 Claude Code 中输入/type-vs-interface确认已加载。

详细介绍

了解 type 和 interface 的区别

概述

对象类型使用 interface,其他情况使用 type。

type 和 interface 都可以定义对象类型,但它们的功能有所不同。了解这些区别有助于你选择合适的工具并编写风格一致的代码。

何时使用此技能

  • 定义任何命名类型
  • 在类型别名和接口之间做选择
  • 扩展或组合类型
  • 处理声明文件
  • 理解库的类型定义

铁律

Use INTERFACE for object types that could be extended.
Use TYPE for unions, tuples, mapped types, and complex compositions.

请记住:

  • 接口支持声明合并
  • 类型别名可以表示更复杂的类型
  • 两者都可以扩展和实现
  • 在代码库中保持一致最重要

快速决策指南

场景使用
对象类型(API 响应、props)interface
联合类型type
元组类型type
函数类型type
映射类型type
原始类型别名type
旨在供扩展的库类型interface

相似之处

两者都能定义对象形状
type TState = {
  name: string;
  capital: string;
};

interface IState {
  name: string;
  capital: string;
}
// These are interchangeable for most purposes
两者都支持泛型
type TBox<T> = { value: T };
interface IBox<T> { value: T }
两者都可以扩展
// Interface extending type
interface IStateWithPop extends TState {
  population: number;
}

// Type extending interface
type TStateWithPop = IState & { population: number };
两者都可以实现
class StateImpl implements IState {
  name = '';
  capital = '';
}

区别

1. 联合类型(仅限 type)
type StringOrNumber = string | number;  // Only with type
type Status = 'pending' | 'fulfilled' | 'rejected';

// Can't do this with interface
2. 元组类型(仅限 type)
type Pair = [number, number];
type NamedNums = [string, ...number[]];

// Interface syntax is awkward
interface IPair {
  0: number;
  1: number;
  length: 2;
}
3. 声明合并(仅限 interface)
interface User {
  name: string;
}

interface User {
  email: string;
}

// Now User has both name and email
const user: User = { name: 'Alice', email: '[email protected]' };

TypeScript 正是通过这种方式在不同 ES 版本间扩展标准库类型。

4. 更好的错误消息(interface)
interface Person {
  name: string;
  age: string;  // Note: string
}

// Type intersection silently creates unusable type
type TPerson = Person & { age: number };  // No error, but age is never

// Interface extension gives helpful error
interface IPerson extends Person {
  age: number;
  // ~~~ Types of property 'age' are incompatible
}
5. 类型别名内联

TypeScript 可能会在错误消息和 .d.ts 文件中内联类型别名:

// In generated .d.ts, type aliases may be expanded
type Point = { x: number; y: number };
export function getOrigin(): Point { ... }

// May become:
export function getOrigin(): { x: number; y: number };

// Interfaces are preserved by name
interface IPoint { x: number; y: number }
export function getOrigin(): IPoint { ... }

// Stays as:
export function getOrigin(): IPoint;

何时使用 interface

定义公共 API
// Users might want to extend this
export interface RequestOptions {
  url: string;
  method?: string;
  headers?: Record<string, string>;
}

// Declaration merging allows extension
declare module 'my-lib' {
  interface RequestOptions {
    timeout?: number;  // User adds this
  }
}
一般对象类型
interface User {
  id: string;
  name: string;
  email: string;
}

interface Post {
  id: string;
  title: string;
  author: User;
}

何时使用 type

联合类型
type Result<T> = { ok: true; value: T } | { ok: false; error: Error };
type Status = 'loading' | 'success' | 'error';
元组和数组
type Point = [x: number, y: number];
type RGB = [number, number, number];
函数类型
type Handler = (event: Event) => void;
type AsyncFn<T> = () => Promise<T>;
映射类型和条件类型
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type NonNullable<T> = T extends null | undefined ? never : T;
计算类型
type Keys = keyof User;  // 'id' | 'name' | 'email'
type UserValues = User[keyof User];  // string

扩展模式

接口扩展接口
interface Animal {
  name: string;
}

interface Dog extends Animal {
  breed: string;
}
类型交叉类型
type Animal = { name: string };
type Dog = Animal & { breed: string };
混合使用(两种方式都可行)
// Interface extends type
interface Dog extends Animal { breed: string; }

// Type extends interface
type Cat = Animal & { meows: boolean };

抵御压力原则

1. “选一个,然后坚持使用”

压力:“保持一致比选择什么更重要”

**回应:**没错,但要清楚自己为什么这样选。

**行动:**对象默认使用 interface,其他情况使用 type。

2. “我需要接口的联合类型”

压力:“我的接口应该构成一个联合类型”

**回应:**你可以用类型别名对接口进行联合。

行动:type Either = InterfaceA | InterfaceB;

警示信号——停止并重新考虑

  • 使用 interface 定义元组类型
  • 需要声明合并时却使用 type
  • 在代码库中对相似类型的用法不一致

常见的自我辩解(均不成立)

借口实际情况
“它们完全一样”不,声明合并和联合类型方面存在差异
“Type 总是更好”Interface 能提供更好的错误消息,并支持声明合并
“Interface 总是更好”它无法表示联合类型、元组或映射类型

快速参考

// USE INTERFACE FOR:
interface User { name: string; }       // Object types
interface Config extends Base { }       // Extendable types

// USE TYPE FOR:
type ID = string | number;              // Unions
type Point = [number, number];          // Tuples
type Handler = () => void;              // Functions
type Keys = keyof User;                 // Computed types
type Mapped = { [K in Keys]: boolean }; // Mapped types

总结

对象类型使用 interface,其他情况使用 type。

接口支持声明合并,产生的错误消息也更好。联合类型、元组和复杂的类型操作则需要类型别名。在代码库中保持一致。

参考资料

根据 Dan Vanderkam 所著的《Effective TypeScript》第 13 条:“了解 type 和 interface 的区别”。

评论

支持 Markdown