TypeScript 高级类型详解

TypeScript 的类型系统是其最强大的特性之一。本文将深入探讨 TypeScript 的高级类型,帮助你写出更类型安全、更易维护的代码。

1. 泛型(Generics)

泛型是 TypeScript 中最强大的特性之一,它允许我们编写可重用的代码。

基础泛型

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

// 使用
const num = identity<number>(42);
const str = identity<string>("hello");

// 类型推断
const inferred = identity(42); // T 被推断为 number

泛型约束

interface HasLength {
  length: number;
}

// 约束 T 必须具有 length 属性
function logLength<T extends HasLength>(arg: T): T {
  console.log(arg.length);
  return arg;
}

logLength("hello");     // ✅ 字符串有 length
logLength([1, 2, 3]);   // ✅ 数组有 length
logLength({ length: 10 }); // ✅ 对象有 length
// logLength(42);       // ❌ number 没有 length

泛型接口

interface GenericResponse<T> {
  data: T;
  status: number;
  message: string;
}

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

const userResponse: GenericResponse<User> = {
  data: { id: 1, name: "John" },
  status: 200,
  message: "Success"
};

泛型类

class GenericStack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }

  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }
}

// 使用
const numberStack = new GenericStack<number>();
numberStack.push(1);
numberStack.push(2);
const top = numberStack.pop(); // number | undefined

2. 条件类型(Conditional Types)

条件类型允许我们根据类型关系选择类型。

// 基础条件类型
type IsString<T> = T extends string ? true : false;

type A = IsString<string>;  // true
type B = IsString<number>;  // false

// 实际应用:根据类型返回不同结果
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;

interface Email {
  message: string;
}

interface Dog {
  bark(): void;
}

type EmailMessage = MessageOf<Email>; // string
type DogMessage = MessageOf<Dog>;     // never

infer 关键字

// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function greet() {
  return "hello";
}

type GreetReturn = ReturnType<typeof greet>; // string

// 提取 Promise 解析类型
type Awaited<T> = T extends Promise<infer U> ? U : T;

type PromiseValue = Awaited<Promise<string>>; // string

3. 映射类型(Mapped Types)

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

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

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

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

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

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

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

type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number; readonly email?: string; }

键重映射

// 添加前缀
type AddPrefix<T, P extends string> = {
  [K in keyof T as `${P}${string & K}`]: T[K];
};

interface Config {
  host: string;
  port: number;
}

type EnvConfig = AddPrefix<Config, "APP_">;
// { APP_host: string; APP_port: number; }

// 过滤特定类型
type StringProperties<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K];
};

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

type StringProps = StringProperties<Person>;
// { name: string; email: string; }

4. 模板字面量类型

type EventName<T extends string> = `on${Capitalize<T>}`;

type ClickEvent = EventName<"click">; // "onClick"
type HoverEvent = EventName<"hover">; // "onHover"

// 组合多个字面量
type Alignment = "top" | "bottom" | "left" | "right";
type MarginProperty = `margin${Capitalize<Alignment>}`;
// "marginTop" | "marginBottom" | "marginLeft" | "marginRight"

5. 实用工具类型

Pick 和 Omit

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
}

// 选取特定属性
type PublicUser = Pick<User, "id" | "name" | "email">;
// { id: number; name: string; email: string; }

// 排除特定属性
type SafeUser = Omit<User, "password">;
// { id: number; name: string; email: string; createdAt: Date; }

Record

// 创建具有特定键和值类型的对象
type PageInfo = {
  title: string;
  path: string;
};

type PageMap = Record<string, PageInfo>;

const pages: PageMap = {
  home: { title: "Home", path: "/" },
  about: { title: "About", path: "/about" },
};

Exclude 和 Extract

type T = "a" | "b" | "c" | "d";

// 排除特定类型
type ExcludeCD = Exclude<T, "c" | "d">; // "a" | "b"

// 提取特定类型
type ExtractAB = Extract<T, "a" | "b">; // "a" | "b"

Parameters 和 ReturnType

function greet(name: string, age: number): string {
  return `Hello ${name}, you are ${age}`;
}

// 获取参数类型
type GreetParams = Parameters<typeof greet>;
// [name: string, age: number]

// 获取返回类型
type GreetReturn = ReturnType<typeof greet>;
// string

6. 类型守卫(Type Guards)

// typeof 类型守卫
function processValue(value: string | number) {
  if (typeof value === "string") {
    return value.toUpperCase(); // TypeScript 知道这里是 string
  }
  return value.toFixed(2); // TypeScript 知道这里是 number
}

// instanceof 类型守卫
class Dog {
  bark() {
    console.log("Woof!");
  }
}

class Cat {
  meow() {
    console.log("Meow!");
  }
}

function makeSound(animal: Dog | Cat) {
  if (animal instanceof Dog) {
    animal.bark();
  } else {
    animal.meow();
  }
}

// 自定义类型守卫
interface Fish {
  swim(): void;
}

interface Bird {
  fly(): void;
}

function isFish(animal: Fish | Bird): animal is Fish {
  return (animal as Fish).swim !== undefined;
}

function move(animal: Fish | Bird) {
  if (isFish(animal)) {
    animal.swim();
  } else {
    animal.fly();
  }
}

7. 联合类型和交叉类型

联合类型

type Status = "pending" | "success" | "error";
type ID = string | number;

interface SuccessResult {
  status: "success";
  data: unknown;
}

interface ErrorResult {
  status: "error";
  error: string;
}

type Result = SuccessResult | ErrorResult;

function handleResult(result: Result) {
  if (result.status === "success") {
    console.log(result.data);
  } else {
    console.error(result.error);
  }
}

交叉类型

interface HasName {
  name: string;
}

interface HasAge {
  age: number;
}

type Person = HasName & HasAge;
// { name: string; age: number; }

const person: Person = {
  name: "John",
  age: 30,
};

8. 递归类型

// 树形结构
type TreeNode<T> = {
  value: T;
  children?: TreeNode<T>[];
};

const tree: TreeNode<string> = {
  value: "root",
  children: [
    {
      value: "child1",
      children: [
        { value: "grandchild1" },
        { value: "grandchild2" },
      ],
    },
    { value: "child2" },
  ],
};

// JSON 类型
type JSONValue = 
  | string 
  | number 
  | boolean 
  | null 
  | JSONValue[] 
  | { [key: string]: JSONValue };

const json: JSONValue = {
  name: "John",
  age: 30,
  hobbies: ["reading", "gaming"],
  address: {
    city: "New York",
    zip: "10001",
  },
};

9. 类型挑战示例

深度 Readonly

type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object 
    ? DeepReadonly<T[P]> 
    : T[P];
};

interface NestedObject {
  a: {
    b: {
      c: string;
    };
  };
}

type ReadonlyNested = DeepReadonly<NestedObject>;
// 所有嵌套属性都变为 readonly

元组转联合类型

type TupleToUnion<T extends readonly unknown[]> = T[number];

type Result = TupleToUnion<["a", "b", "c"]>;
// "a" | "b" | "c"

可选链式调用类型

type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

// 使用
interface AppConfig {
  server: {
    host: string;
    port: number;
    ssl: {
      cert: string;
      key: string;
    };
  };
}

type PartialConfig = DeepPartial<AppConfig>;
// 所有属性都变为可选,包括嵌套的

总结

TypeScript 高级类型的核心概念:

  1. 泛型 - 编写可重用的类型安全代码
  2. 条件类型 - 基于类型关系选择类型
  3. 映射类型 - 基于旧类型创建新类型
  4. 模板字面量类型 - 操作字符串类型
  5. 类型守卫 - 运行时类型检查
  6. 联合/交叉类型 - 组合类型

掌握这些高级类型,你将能够:

  • 构建更类型安全的应用
  • 减少运行时错误
  • 提高代码可维护性
  • 编写更好的库类型定义

本文首发于技术博客,转载请注明出处。