未找到匹配的笔记

高级类型操作

类型系统高级特性

高级类型操作

TypeScript 提供了强大的类型操作能力,可以在类型层面进行编程,创建灵活且类型安全的代码。

映射类型(Mapped Types)

映射类型允许基于旧类型创建新类型。

基础映射类型

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

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

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

type PartialUser = Partial<User>;
// 等同于 { id?: number; name?: string; email?: string; }

type ReadonlyUser = Readonly<User>;
// 等同于 { readonly id: number; readonly name: string; readonly email: string; }

自定义映射类型

// 将所有属性变为必需且非空
type Required<T> = {
  [P in keyof T]-?: T[P];
};

// 移除 readonly 修饰符
type Writable<T> = {
  -readonly [P in keyof T]: T[P];
};

// 选择特定属性
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

// 排除特定属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// 使用示例
interface Todo {
  title: string;
  description: string;
  completed: boolean;
  createdAt: number;
}

type TodoPreview = Pick<Todo, "title" | "completed">;
// { title: string; completed: boolean; }

type TodoInfo = Omit<Todo, "completed" | "createdAt">;
// { title: string; description: string; }

键重映射(Key Remapping)

// TypeScript 4.1+ 支持键重映射
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

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

type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number; }

// 过滤特定键
type RemoveKindField<T> = {
  [K in keyof T as Exclude<K, "kind">]: T[K];
};

interface Circle {
  kind: "circle";
  radius: number;
}

type KindlessCircle = RemoveKindField<Circle>;
// { radius: number; }

条件类型(Conditional Types)

条件类型允许根据条件选择类型。

基础条件类型

// 语法:T extends U ? X : Y
type IsArray<T> = T extends any[] ? true : false;

type A = IsArray<number[]>;    // true
type B = IsArray<string>;      // false

// 提取数组元素类型
type ArrayElementType<T> = T extends (infer U)[] ? U : never;

type E1 = ArrayElementType<string[]>;  // string
type E2 = ArrayElementType<number[]>;  // number

infer 关键字

// infer 用于在条件类型中推断类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;

type Func = () => string;
type R = ReturnType<Func>; // string

// 提取函数参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

type Func2 = (a: number, b: string) => void;
type Params = Parameters<Func2>; // [number, string]

// 提取第一个参数类型
type FirstParameter<T> = T extends (first: infer F, ...args: any[]) => any 
  ? F 
  : never;

分布式条件类型

// 当条件类型作用于联合类型时,会分布式应用
type ToArray<T> = T extends any ? T[] : never;

type StrArrOrNumArr = ToArray<string | number>;
// string[] | number[](不是 (string | number)[])

// 阻止分布式行为
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;

type StrArrOrNumArr2 = ToArrayNonDist<string | number>;
// (string | number)[]

实用条件类型示例

// 检查是否为 never 类型
type IsNever<T> = [T] extends [never] ? true : false;

// 检查两个类型是否完全相同
type Equals<X, Y> = 
  (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? true : false;

// 提取 Promise 的返回类型
type Awaited<T> = T extends Promise<infer U> ? U : T;

type P = Promise<string>;
type Result = Awaited<P>; // string

模板字面量类型(Template Literal Types)

TypeScript 4.1+ 引入了模板字面量类型,可以在类型层面操作字符串。

基础用法

type World = "world";
type Greeting = `hello ${World}`; // "hello world"

type EmailLocaleIDs = "welcome_email" | "email_heading";
type FooterLocaleIDs = "footer_title" | "footer_sendoff";

type AllLocaleIDs = `${EmailLocaleIDs | FooterLocaleIDs}_id`;
// "welcome_email_id" | "email_heading_id" | "footer_title_id" | "footer_sendoff_id"

实用工具类型

// 首字母大写
type Capitalize<S extends string> = intrinsic;

// 首字母小写
type Uncapitalize<S extends string> = intrinsic;

// 全部大写
type Uppercase<S extends string> = intrinsic;

// 全部小写
type Lowercase<S extends string> = intrinsic;

type T1 = Capitalize<"hello">;      // "Hello"
type T2 = Uppercase<"hello">;       // "HELLO"
type T3 = Lowercase<"HELLO">;       // "hello"

实际应用

// 创建事件处理器类型
type EventName<T extends string> = `on${Capitalize<T>}`;

type ClickEvent = EventName<"click">;    // "onClick"
type FocusEvent = EventName<"focus">;    // "onFocus"

// 创建属性访问器
type Getter<T extends string> = `get${Capitalize<T>}`;
type Setter<T extends string> = `set${Capitalize<T>}`;

type NameGetter = Getter<"name">;        // "getName"
type NameSetter = Setter<"name">;        // "setName"

// 路径字符串类型
type Path = `/${string}`;
type ApiPath = `/api/${string}`;

const path1: Path = "/users";           // ✅
const path2: ApiPath = "/api/users";    // ✅

实用工具类型(Utility Types)

TypeScript 内置了许多实用工具类型。

Partial 和 Required

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

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

type RequiredUser = Required<User>;
// { id: number; name: string; email: string; }

Readonly 和 Writable

interface Config {
  apiUrl: string;
  timeout: number;
}

type ReadonlyConfig = Readonly<Config>;
// { readonly apiUrl: string; readonly timeout: number; }

Pick 和 Omit

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;
// { title: string; completed: boolean; }

type TodoInfo = Omit<Todo, "completed">;
// { title: string; description: string; }

Record

// 创建具有特定键类型的对象类型
type Record<K extends keyof any, T> = {
  [P in K]: T;
};

type PageInfo = {
  title: string;
};

type Page = "home" | "about" | "contact";

const x: Record<Page, PageInfo> = {
  home: { title: "Home" },
  about: { title: "About" },
  contact: { title: "Contact" }
};

Exclude 和 Extract

// 从联合类型中排除某些类型
type T0 = Exclude<"a" | "b" | "c", "a">;        // "b" | "c"
type T1 = Exclude<string | number | (() => void), Function>; // string | number

// 从联合类型中提取某些类型
type T2 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T3 = Extract<string | number | (() => void), Function>; // () => void

NonNullable

// 排除 null 和 undefined
type T0 = NonNullable<string | number | undefined>; // string | number
type T1 = NonNullable<string[] | null | undefined>; // string[]

Parameters 和 ReturnType

declare function f1(arg: { a: number; b: string }): void;

type T0 = Parameters<typeof f1>;
// [{ a: number; b: string }]

type T1 = ReturnType<typeof f1>;
// void

ConstructorParameters 和 InstanceType

class C {
  constructor(x: number, y: string) {}
}

type T0 = ConstructorParameters<typeof C>;
// [x: number, y: string]

type T1 = InstanceType<typeof C>;
// C

Awaited(TypeScript 4.5+)

// 提取 Promise 的返回类型
type A = Awaited<Promise<string>>;        // string
type B = Awaited<Promise<Promise<number>>>; // number
type C = Awaited<boolean | Promise<number>>; // boolean | number

类型操作组合

// 创建深度只读类型
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

// 创建深度可选类型
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// 创建深度必需类型
type DeepRequired<T> = {
  [P in keyof T]-?: T[P] extends object ? DeepRequired<T[P]> : T[P];
};

// 提取函数类型的最后一个参数
type LastParameter<T> = T extends (...args: infer P) => any
  ? P extends [...any, infer L]
    ? L
    : never
  : never;

实际应用场景

API 响应类型转换

// 将 API 响应转换为前端需要的格式
type ApiResponse<T> = {
  data: T;
  status: number;
  message: string;
};

type UserResponse = ApiResponse<{
  id: number;
  name: string;
  email: string;
}>;

// 提取数据部分
type UserData = UserResponse["data"];

表单字段类型

// 创建表单字段类型
type FormField<T> = {
  value: T;
  error?: string;
  touched: boolean;
};

type FormFields<T> = {
  [K in keyof T]: FormField<T[K]>;
};

interface LoginForm {
  email: string;
  password: string;
}

type LoginFormFields = FormFields<LoginForm>;
// {
//   email: FormField<string>;
//   password: FormField<string>;
// }

最佳实践

  1. 理解映射类型:用于批量转换类型属性
  2. 掌握条件类型:实现复杂的类型逻辑
  3. 利用 infer:从复杂类型中提取信息
  4. 使用模板字面量类型:创建类型安全的字符串操作
  5. 组合使用工具类型:构建复杂的类型系统
  6. 避免过度复杂:保持类型可读性和可维护性