TypeScript条件类型:让类型也能“判断”

最近在写前端项目的时候,遇到一个挺常见的需求:根据传入的不同参数型,返回对应的数据结构。一开始用联合类型硬扛,结果代码越写越乱,类型提示也变得不可靠。后来用了 TypeScript 的条件类型,问题一下子清晰多了。

什么是条件类型?

简单说,TypeScript 条件类型就是让类型系统也能“做判断”。它的语法看起来像三元运算符:

type TypeName<T> = T extends string ? 'string' : 'other';

上面这段代码的意思是:如果类型 T 可以被赋值给 string,那就返回字面量类型 'string',否则返回 'other'。这就像 JS 里的 typeof 判断,只不过发生在编译时的类型层面。

实际场景:API 响应处理

假设你封装了一个请求函数,传入 user 返回用户信息,传入 post 返回文章列表。你希望返回值的类型能自动匹配输入。

type ApiResponse<T> =
  T extends 'user'
    ? { id: number; name: string }
    : T extends 'post'
      ? { id: number; title: string; content: string }
      : never;

这样调用时,TypeScript 就能自动推导:

function fetchApi<T extends string>(type: T): ApiResponse<T> {
  // 模拟请求逻辑
  return {} as any;
}

const userRes = fetchApi('user');
// 类型自动为 { id: number; name: string }

const postRes = fetchApi('post');
// 类型自动为 { id: number; title: string; content: string }

编辑器立马给出精准提示,不用再翻接口文档。

结合分布式条件类型更强大

当条件类型作用于联合类型时,会自动“分发”到每个成员。比如:

type Wrap<T> = T extends string ? [T] : T;

这个特性常用于过滤或映射类型。比如提取对象中所有字符串字段的键:

type StringKeys<T> = {
  [K in keyof T]: T[K] extends string ? K : never;
}[keyof T];

interface Person {
  name: string;
  age: number;
  city: string;
}

type Keys = StringKeys<Person>; // 'name' | 'city'

这在构建高级类型工具时特别有用,比如表单校验、字段映射等。

小心“坑”

条件类型虽然强大,但也容易写出让人看不懂的类型别名。特别是嵌套多层后,报错信息可能变得晦涩。建议配合 infer 使用时,给中间类型起个易懂的名字,方便后期维护。

另外,不要过度设计。如果一个功能用普通接口就能搞定,就没必要上条件类型。类型系统是为了服务代码,而不是增加阅读负担。