引言

TypeScript 的类型系统非常强大,图灵完备。但很多开发者只使用到了其中很小的一部分。本文分享几个我在日常开发中使用的高级类型技巧。

1. 条件类型 (Conditional Types)

条件类型让你可以根据类型关系来选择不同的类型:

type IsString<T> = T extends string ? "yes" : "no";

type A = IsString<"hello">; // "yes"
type B = IsString<42>;      // "no"

实战:提取 Promise 的值类型

type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;

type Result = Awaited<Promise<Promise<number>>>; // number

2. 模板字面量类型

TypeScript 4.1+ 支持在类型层面操作字符串:

type EventName = "click" | "focus" | "blur";
type HandlerName = `on${Capitalize<EventName>}`;
// "onClick" | "onFocus" | "onBlur"

实战:类型安全的路由

type Route =
  | "/"
  | "/posts"
  | `/posts/${string}`
  | "/about";

function navigate(path: Route) {
  // path 现在是类型安全的
}

navigate("/posts/hello-world"); // ✅
navigate("/admin");              // ❌ 类型错误

3. satisfies 操作符

TypeScript 4.9 引入的 satisfies 让你在保持类型推断的同时进行类型检查:

// 使用 satisfies — 既有类型检查,又保留字面量类型
const config = {
  theme: "dark",
  version: 1,
} satisfies Record<string, string | number>;

// config.theme 的类型是 "dark",不是 string!

4. const 类型断言

const colors = ["red", "green", "blue"] as const;
// 类型是 readonly ["red", "green", "blue"]
// 而不是 string[]

type Color = (typeof colors)[number]; // "red" | "green" | "blue"

5. 类型谓词 (Type Predicates)

自定义类型守卫让你的代码更安全:

interface User {
  type: "user";
  name: string;
}

interface Admin {
  type: "admin";
  name: string;
  permissions: string[];
}

type Person = User | Admin;

function isAdmin(person: Person): person is Admin {
  return person.type === "admin";
}

// 使用
if (isAdmin(person)) {
  // TypeScript 知道这里 person 是 Admin
  console.log(person.permissions);
}

总结

这些高级类型技巧可以帮助你:

  • 减少运行时错误 — 在编译期就发现问题
  • 提升开发体验 — 更好的自动补全和类型提示
  • 编写自文档化的代码 — 类型即文档

TypeScript 的类型系统是一座金矿,值得深入挖掘。

小贴士:如果你刚开始接触这些概念,建议一次只学一个,在实际项目中使用熟练后再学下一个。