技术 · 阅读 7 分钟

TypeScript 类型体操入门

TypeScript 的类型系统远比你想象的强大。本文从基础泛型出发,逐步介绍条件类型、映射类型和模板字面量类型,带你领略类型编程的魅力。

为什么要学类型体操?

TypeScript 的类型系统是图灵完备的,这意味着它可以进行复杂的类型计算。掌握高级类型技巧,可以帮助你写出更安全、更灵活的代码,让编译器成为你最好的搭档。

泛型基础

泛型是类型编程的基石。它让函数和类型可以接受类型参数:

// 基础泛型函数
function identity<T>(value: T): T {
  return value;
}

// 泛型约束
function getLength<T extends { length: number }>(value: T): number {
  return value.length;
}

getLength("hello");     // ✅
getLength([1, 2, 3]);   // ✅
getLength(123);          // ❌ 数字没有 length 属性

条件类型

条件类型类似于三元表达式,但作用于类型层面:

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

type A = IsString<string>;  // "yes"
type B = IsString<number>;  // "no"

// 实用案例:提取 Promise 内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type C = UnwrapPromise<Promise<string>>;  // string
type D = UnwrapPromise<number>;            // number

映射类型

映射类型可以基于已有类型生成新类型:

// 将所有属性变为可选
type MyPartial<T> = {
  [K in keyof T]?: T[K];
};

// 将所有属性变为只读
type MyReadonly<T> = {
  readonly [K in keyof T]: T[K];
};

// 实际使用
interface User {
  name: string;
  age: number;
  email: string;
}

type PartialUser = MyPartial<User>;
// { name?: string; age?: number; email?: string; }

模板字面量类型

TypeScript 4.1 引入的模板字面量类型,可以对字符串类型进行模式匹配和组合:

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

// 更复杂的例子:生成 getter 名称
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number; getEmail: () => string; }

实战练习

推荐几个练习类型体操的资源:

总结

类型体操不是炫技,而是为了写出更健壮的代码。从日常开发中的简单泛型开始,逐步尝试更复杂的类型编程,你会发现 TypeScript 的类型系统是一个充满乐趣的世界。