type、interface 与泛型

type 和 interface 的区别

两者都能描述对象,并且多数场景可以互换。

interface User {
  id: number;
  name: string;
}

type UserWithRole = User & {
  role: 'admin' | 'member';
};

主要差异:

能力interfacetype
描述对象、函数支持支持
继承/组合extends交叉类型 &
联合类型不支持支持
声明合并支持不支持
映射、条件类型不直接支持支持

选择建议:

  • 对外暴露、可能被扩展的对象契约优先 interface
  • 联合类型、工具类型和复杂组合优先 type
  • 团队一致性比机械坚持某一种更重要。

泛型解决什么问题

泛型让类型之间的关系由调用方决定,同时保留推导能力。

function first<T>(items: T[]): T | undefined {
  return items[0];
}

const name = first(['A', 'B']); // string | undefined

不用泛型而返回 any 会丢失输入与输出的关系。

泛型约束

function getProperty<T, K extends keyof T>(object: T, key: K): T[K] {
  return object[key];
}

const user = { id: 1, name: 'Ada' };
getProperty(user, 'name'); // string
// getProperty(user, 'age'); // 编译错误

这里的关键关系是:K 必须是 T 的键,返回值则是对应属性的类型。

keyof、typeof 与索引访问类型

const config = {
  theme: 'dark',
  pageSize: 20,
} as const;

type Config = typeof config;
type ConfigKey = keyof Config;          // 'theme' | 'pageSize'
type Theme = Config['theme'];           // 'dark'

注意 TypeScript 的 typeof 出现在类型位置时用于获取变量的静态类型,不等同于运行时 typeof 运算符。

组件泛型示例

type SelectProps<T> = {
  options: T[];
  getKey: (item: T) => string;
  renderLabel: (item: T) => string;
  onChange: (item: T) => void;
};

function createSelect<T>(props: SelectProps<T>) {
  return props.options.map((item) => ({
    key: props.getKey(item),
    label: props.renderLabel(item),
  }));
}

设计泛型 API 时,应让 TypeScript 尽量从参数推导类型,避免要求调用者重复书写泛型参数。