TypeScript

A 级 · 高频考点 · 重点掌握 type vs interface、泛型和工具类型

1 类型基础

type 和 interface 的区别?什么时候用哪个?
interface:只能描述对象结构,支持 extends 继承和声明合并(同名 interface 自动合并)。
type:可以描述任何类型(联合、交叉、原始类型别名、元组),用 & 交叉实现"继承",不能声明合并

选择建议:定义组件 Props 和 API 契约用 interface(可扩展);定义联合类型、工具类型用 type。团队统一即可。

// interface — 声明合并
interface User { name: string; }
interface User { age: number; }
// 等价于 { name: string; age: number; }

// type — 联合类型
type Status = 'loading' | 'success' | 'error';
type Result = User & { status: Status };
联合类型 vs 交叉类型?
联合类型 A | B:值是 A B 的其中一个,只能访问共有属性。
交叉类型 A & B:值同时满足 A B,拥有所有属性。
类型断言 vs 类型守卫?
类型断言(as):告诉编译器"我知道它是什么类型",不做运行时检查,可能不安全。
类型守卫:通过运行时检查收窄类型,更安全。如 typeof、instanceof、in、自定义 is 谓词。
// 类型断言 — 不安全
const el = document.getElementById('app') as HTMLDivElement;

// 类型守卫 — 安全
function isString(x: unknown): x is string {
  return typeof x === 'string';
}
if (isString(value)) {
  value.toUpperCase(); // TS 知道这里是 string
}

2 泛型

泛型是什么?为什么需要?
泛型是类型的参数,让函数/接口/类在定义时不指定具体类型,使用时再传入。好处是复用代码 + 保持类型安全,避免用 any 丧失类型检查。
// 不用泛型 — 丧失类型信息
function identity(arg: any): any { return arg; }

// 用泛型 — 保持类型安全
function identity<T>(arg: T): T { return arg; }
identity<string>('hello'); // 返回类型自动推断为 string
identity(42);              // T 推断为 number
泛型约束 extends?
<T extends SomeType> 限制泛型参数必须满足某种结构,既保留灵活性又确保类型安全。
// 约束 T 必须有 length 属性
function logLength<T extends { length: number }>(arg: T): T {
  console.log(arg.length);
  return arg;
}
logLength('hello');  // ✅ string 有 length
logLength([1, 2]);   // ✅ array 有 length
logLength(42);       // ❌ number 没有 length

3 工具类型

常用内置工具类型?
Partial<T>:所有属性变可选
Required<T>:所有属性变必填
Pick<T, K>:只保留指定属性
Omit<T, K>:排除指定属性
Record<K, V>:构造键值对类型
ReturnType<T>:提取函数返回类型
Parameters<T>:提取函数参数类型(元组)
interface User {
  id: number;
  name: string;
  email: string;
}

type UserPreview = Pick<User, 'id' | 'name'>;
type UserUpdate = Partial<Omit<User, 'id'>>;
// { name?: string; email?: string; }
Partial 的实现原理?
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};
// keyof T — 获取 T 的所有键组成的联合类型
// [K in ...] — 映射类型,遍历每个键
// ? — 标记为可选

4 高级类型

条件类型?infer 关键字?
T extends U ? X : Y — 类型层面的三元表达式。
infer — 在条件类型中提取/推断类型。
// 提取 Promise 的返回类型
type Unwrap<T> = T extends Promise<infer R> ? R : T;
type A = Unwrap<Promise<string>>; // string
type B = Unwrap<number>;           // number

// ReturnType 的实现原理
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
模板字面量类型?
TS 4.1+ 支持在类型中使用模板字面量语法,可以动态生成字符串类型。
type EventName = 'click' | 'scroll' | 'focus';
type Handler = `on${Capitalize<EventName>}`;
// 'onClick' | 'onScroll' | 'onFocus'

5 类型收窄

TS 有哪些类型收窄方式?
typeof:收窄原始类型
instanceof:收窄类实例
in:检查属性是否存在
可辨识联合:通过共有的字面量属性(如 type: 'success' | 'error')收窄
自定义类型守卫function isX(v): v is X
// 可辨识联合 — 最实用的收窄方式
type Success = { type: 'success'; data: string };
type Error = { type: 'error'; message: string };
type Result = Success | Error;

function handle(result: Result) {
  switch (result.type) {
    case 'success': return result.data;    // TS 知道是 Success
    case 'error': return result.message;   // TS 知道是 Error
  }
}