typescript-advanced

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeScript Advanced Types

TypeScript 高级类型

Comprehensive guidance for mastering TypeScript's advanced type system including generics, conditional types, mapped types, template literal types, and utility types for building robust, type-safe applications.
本指南全面介绍如何掌握TypeScript的高级类型系统,包括泛型、条件类型、映射类型、模板字面量类型和工具类型,用于构建健壮、类型安全的应用程序。

Quick Reference

快速参考

See references/best-practices.md for practical guidelines on:
  • Configuration: Essential
    tsconfig.json
    settings (
    strict
    ,
    noUncheckedIndexedAccess
    ,
    exactOptionalPropertyTypes
    )
  • Type Inference vs Explicit Types: When to annotate vs let TypeScript infer
  • Interfaces vs Types: Strategic guidelines for choosing between them
  • Module/Path Mapping: tsconfig paths and barrel exports patterns
  • Error Handling: Custom error classes and Result pattern
  • Performance: Type system performance considerations
  • Type Testing:
    Expect<Equal<X, Y>>
    patterns for compile-time type tests
查看 references/best-practices.md 获取以下实用指南:
  • 配置:必要的
    tsconfig.json
    设置(
    strict
    noUncheckedIndexedAccess
    exactOptionalPropertyTypes
  • 类型推断 vs 显式类型:何时进行类型标注 vs 让TypeScript自动推断
  • 接口 vs 类型别名:选择两者的策略指南
  • 模块/路径映射:tsconfig路径和桶导出模式
  • 错误处理:自定义错误类和Result模式
  • 性能:类型系统的性能考量
  • 类型测试:用于编译时类型测试的
    Expect<Equal<X, Y>>
    模式

When to Use This Skill

适用场景

  • Building type-safe libraries or frameworks
  • Creating reusable generic components
  • Implementing complex type inference logic
  • Designing type-safe API clients
  • Building form validation systems
  • Creating strongly-typed configuration objects
  • Implementing type-safe state management
  • Migrating JavaScript codebases to TypeScript
  • 构建类型安全的库或框架
  • 创建可复用的泛型组件
  • 实现复杂的类型推断逻辑
  • 设计类型安全的API客户端
  • 构建表单验证系统
  • 创建强类型的配置对象
  • 实现类型安全的状态管理
  • 将JavaScript代码库迁移到TypeScript

Core Concepts

核心概念

1. Generics

1. 泛型

Purpose: Create reusable, type-flexible components while maintaining type safety.
Basic Generic Function:
typescript
function identity<T>(value: T): T {
  return value;
}

const num = identity<number>(42); // Type: number
const str = identity<string>("hello"); // Type: string
const auto = identity(true); // Type inferred: boolean
Generic Constraints:
typescript
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(item: T): T {
  console.log(item.length);
  return item;
}

logLength("hello"); // OK: string has length
logLength([1, 2, 3]); // OK: array has length
logLength({ length: 10 }); // OK: object has length
// logLength(42);             // Error: number has no length
Multiple Type Parameters:
typescript
function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

const merged = merge({ name: "John" }, { age: 30 });
// Type: { name: string } & { age: number }
用途:创建可复用、类型灵活的组件,同时保持类型安全。
基础泛型函数
typescript
function identity<T>(value: T): T {
  return value;
}

const num = identity<number>(42); // 类型:number
const str = identity<string>("hello"); // 类型:string
const auto = identity(true); // 自动推断类型:boolean
泛型约束
typescript
interface HasLength {
  length: number;
}

function logLength<T extends HasLength>(item: T): T {
  console.log(item.length);
  return item;
}

logLength("hello"); // 合法:string具有length属性
logLength([1, 2, 3]); // 合法:array具有length属性
logLength({ length: 10 }); // 合法:object具有length属性
// logLength(42);             // 错误:number没有length属性
多类型参数
typescript
function merge<T, U>(obj1: T, obj2: U): T & U {
  return { ...obj1, ...obj2 };
}

const merged = merge({ name: "John" }, { age: 30 });
// 类型:{ name: string } & { age: number }

2. Conditional Types

2. 条件类型

Purpose: Create types that depend on conditions, enabling sophisticated type logic.
Basic Conditional Type:
typescript
type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false
Extracting Return Types:
typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function getUser() {
  return { id: 1, name: "John" };
}

type User = ReturnType<typeof getUser>;
// Type: { id: number; name: string; }
Distributive Conditional Types:
typescript
type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArray = ToArray<string | number>;
// Type: string[] | number[]
Nested Conditions:
typescript
type TypeName<T> = T extends string
  ? "string"
  : T extends number
    ? "number"
    : T extends boolean
      ? "boolean"
      : T extends undefined
        ? "undefined"
        : T extends Function
          ? "function"
          : "object";

type T1 = TypeName<string>; // "string"
type T2 = TypeName<() => void>; // "function"
用途:创建依赖于条件的类型,实现复杂的类型逻辑。
基础条件类型
typescript
type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true
type B = IsString<number>; // false
提取返回类型
typescript
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function getUser() {
  return { id: 1, name: "John" };
}

type User = ReturnType<typeof getUser>;
// 类型:{ id: number; name: string; }
分布式条件类型
typescript
type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArray = ToArray<string | number>;
// 类型:string[] | number[]
嵌套条件
typescript
type TypeName<T> = T extends string
  ? "string"
  : T extends number
    ? "number"
    : T extends boolean
      ? "boolean"
      : T extends undefined
        ? "undefined"
        : T extends Function
          ? "function"
          : "object";

type T1 = TypeName<string>; // "string"
type T2 = TypeName<() => void>; // "function"

3. Mapped Types

3. 映射类型

Purpose: Transform existing types by iterating over their properties.
Basic Mapped Type:
typescript
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

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

type ReadonlyUser = Readonly<User>;
// Type: { readonly id: number; readonly name: string; }
Optional Properties:
typescript
type Partial<T> = {
  [P in keyof T]?: T[P];
};

type PartialUser = Partial<User>;
// Type: { id?: number; name?: string; }
Key Remapping:
typescript
type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

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

type PersonGetters = Getters<Person>;
// Type: { getName: () => string; getAge: () => number; }
Filtering Properties:
typescript
type PickByType<T, U> = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface Mixed {
  id: number;
  name: string;
  age: number;
  active: boolean;
}

type OnlyNumbers = PickByType<Mixed, number>;
// Type: { id: number; age: number; }
用途:通过遍历现有类型的属性来转换类型。
基础映射类型
typescript
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

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

type ReadonlyUser = Readonly<User>;
// 类型:{ readonly id: number; readonly name: string; }
可选属性
typescript
type Partial<T> = {
   [P in keyof T]?: T[P];
};

type PartialUser = Partial<User>;
// 类型:{ id?: number; name?: string; }
键重映射
typescript
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; }
属性过滤
typescript
type PickByType<T, U> = {
   [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface Mixed {
   id: number;
   name: string;
   age: number;
   active: boolean;
}

type OnlyNumbers = PickByType<Mixed, number>;
// 类型:{ id: number; age: number; }

4. Template Literal Types

4. 模板字面量类型

Purpose: Create string-based types with pattern matching and transformation.
Basic Template Literal:
typescript
type EventName = "click" | "focus" | "blur";
type EventHandler = `on${Capitalize<EventName>}`;
// Type: "onClick" | "onFocus" | "onBlur"
String Manipulation:
typescript
type UppercaseGreeting = Uppercase<"hello">; // "HELLO"
type LowercaseGreeting = Lowercase<"HELLO">; // "hello"
type CapitalizedName = Capitalize<"john">; // "John"
type UncapitalizedName = Uncapitalize<"John">; // "john"
Path Building:
typescript
type Path<T> = T extends object
  ? { [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never }[keyof T]
  : never;

interface Config {
  server: {
    host: string;
    port: number;
  };
  database: {
    url: string;
  };
}

type ConfigPath = Path<Config>;
// Type: "server" | "database" | "server.host" | "server.port" | "database.url"
用途:创建基于字符串的类型,支持模式匹配和转换。
基础模板字面量
typescript
type EventName = "click" | "focus" | "blur";
type EventHandler = `on${Capitalize<EventName>}`;
// 类型:"onClick" | "onFocus" | "onBlur"
字符串操作
typescript
type UppercaseGreeting = Uppercase<"hello">; // "HELLO"
type LowercaseGreeting = Lowercase<"HELLO">; // "hello"
type CapitalizedName = Capitalize<"john">; // "John"
type UncapitalizedName = Uncapitalize<"John">; // "john"
路径构建
typescript
type Path<T> = T extends object
  ? { [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never }[keyof T]
  : never;

interface Config {
   server: {
     host: string;
     port: number;
   };
   database: {
     url: string;
   };
}

type ConfigPath = Path<Config>;
// 类型:"server" | "database" | "server.host" | "server.port" | "database.url"

5. Utility Types

5. 工具类型

Built-in Utility Types:
typescript
// Partial<T> - Make all properties optional
type PartialUser = Partial<User>;

// Required<T> - Make all properties required
type RequiredUser = Required<PartialUser>;

// Readonly<T> - Make all properties readonly
type ReadonlyUser = Readonly<User>;

// Pick<T, K> - Select specific properties
type UserName = Pick<User, "name" | "email">;

// Omit<T, K> - Remove specific properties
type UserWithoutPassword = Omit<User, "password">;

// Exclude<T, U> - Exclude types from union
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"

// Extract<T, U> - Extract types from union
type T2 = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b"

// NonNullable<T> - Exclude null and undefined
type T3 = NonNullable<string | null | undefined>; // string

// Record<K, T> - Create object type with keys K and values T
type PageInfo = Record<"home" | "about", { title: string }>;
内置工具类型
typescript
// Partial<T> - 将所有属性设为可选
type PartialUser = Partial<User>;

// Required<T> - 将所有属性设为必填
type RequiredUser = Required<PartialUser>;

// Readonly<T> - 将所有属性设为只读
type ReadonlyUser = Readonly<User>;

// Pick<T, K> - 选择特定属性
type UserName = Pick<User, "name" | "email">;

// Omit<T, K> - 移除特定属性
type UserWithoutPassword = Omit<User, "password">;

// Exclude<T, U> - 从联合类型中排除类型
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"

// Extract<T, U> - 从联合类型中提取类型
type T2 = Extract<"a" | "b" | "c", "a" | "b">; // "a" | "b"

// NonNullable<T> - 排除null和undefined
type T3 = NonNullable<string | null | undefined>; // string

// Record<K, T> - 创建键为K、值为T的对象类型
type PageInfo = Record<"home" | "about", { title: string }>;

Advanced Patterns

高级模式

Pattern 1: Type-Safe Event Emitter

模式1:类型安全的事件发射器

typescript
type EventMap = {
  "user:created": { id: string; name: string };
  "user:updated": { id: string };
  "user:deleted": { id: string };
};

class TypedEventEmitter<T extends Record<string, any>> {
  private listeners: {
    [K in keyof T]?: Array<(data: T[K]) => void>;
  } = {};

  on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {
    if (!this.listeners[event]) {
      this.listeners[event] = [];
    }
    this.listeners[event]!.push(callback);
  }

  emit<K extends keyof T>(event: K, data: T[K]): void {
    const callbacks = this.listeners[event];
    if (callbacks) {
      callbacks.forEach(callback => callback(data));
    }
  }
}

const emitter = new TypedEventEmitter<EventMap>();

emitter.on("user:created", data => {
  console.log(data.id, data.name); // Type-safe!
});

emitter.emit("user:created", { id: "1", name: "John" });
// emitter.emit("user:created", { id: "1" });  // Error: missing 'name'
typescript
type EventMap = {
   "user:created": { id: string; name: string };
   "user:updated": { id: string };
   "user:deleted": { id: string };
};

class TypedEventEmitter<T extends Record<string, any>> {
   private listeners: {
     [K in keyof T]?: Array<(data: T[K]) => void>;
   } = {};

   on<K extends keyof T>(event: K, callback: (data: T[K]) => void): void {
     if (!this.listeners[event]) {
       this.listeners[event] = [];
     }
     this.listeners[event]!.push(callback);
   }

   emit<K extends keyof T>(event: K, data: T[K]): void {
     const callbacks = this.listeners[event];
     if (callbacks) {
       callbacks.forEach(callback => callback(data));
     }
   }
}

const emitter = new TypedEventEmitter<EventMap>();

emitter.on("user:created", data => {
   console.log(data.id, data.name); // 类型安全!
});

emitter.emit("user:created", { id: "1", name: "John" });
// emitter.emit("user:created", { id: "1" });  // 错误:缺少'name'

Pattern 2: Type-Safe API Client

模式2:类型安全的API客户端

typescript
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";

type EndpointConfig = {
  "/users": {
    GET: { response: User[] };
    POST: { body: { name: string; email: string }; response: User };
  };
  "/users/:id": {
    GET: { params: { id: string }; response: User };
    PUT: { params: { id: string }; body: Partial<User>; response: User };
    DELETE: { params: { id: string }; response: void };
  };
};

type ExtractParams<T> = T extends { params: infer P } ? P : never;
type ExtractBody<T> = T extends { body: infer B } ? B : never;
type ExtractResponse<T> = T extends { response: infer R } ? R : never;

class APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {
  async request<Path extends keyof Config, Method extends keyof Config[Path]>(
    path: Path,
    method: Method,
    ...[options]: ExtractParams<Config[Path][Method]> extends never
      ? ExtractBody<Config[Path][Method]> extends never
        ? []
        : [{ body: ExtractBody<Config[Path][Method]> }]
      : [
          {
            params: ExtractParams<Config[Path][Method]>;
            body?: ExtractBody<Config[Path][Method]>;
          },
        ]
  ): Promise<ExtractResponse<Config[Path][Method]>> {
    // Implementation here
    return {} as any;
  }
}

const api = new APIClient<EndpointConfig>();

// Type-safe API calls
const users = await api.request("/users", "GET");
// Type: User[]

const newUser = await api.request("/users", "POST", {
  body: { name: "John", email: "john@example.com" },
});
// Type: User

const user = await api.request("/users/:id", "GET", {
  params: { id: "123" },
});
// Type: User
typescript
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";

type EndpointConfig = {
   "/users": {
     GET: { response: User[] };
     POST: { body: { name: string; email: string }; response: User };
   };
   "/users/:id": {
     GET: { params: { id: string }; response: User };
     PUT: { params: { id: string }; body: Partial<User>; response: User };
     DELETE: { params: { id: string }; response: void };
   };
};

type ExtractParams<T> = T extends { params: infer P } ? P : never;
type ExtractBody<T> = T extends { body: infer B } ? B : never;
type ExtractResponse<T> = T extends { response: infer R } ? R : never;

class APIClient<Config extends Record<string, Record<HTTPMethod, any>>> {
   async request<Path extends keyof Config, Method extends keyof Config[Path]>(
     path: Path,
     method: Method,
     ...[options]: ExtractParams<Config[Path][Method]> extends never
       ? ExtractBody<Config[Path][Method]> extends never
         ? []
         : [{ body: ExtractBody<Config[Path][Method]> }]
       : [
           {
             params: ExtractParams<Config[Path][Method]>;
             body?: ExtractBody<Config[Path][Method]>;
           },
         ]
   ): Promise<ExtractResponse<Config[Path][Method]>> {
     // 实现代码
     return {} as any;
   }
}

const api = new APIClient<EndpointConfig>();

// 类型安全的API调用
const users = await api.request("/users", "GET");
// 类型:User[]

const newUser = await api.request("/users", "POST", {
   body: { name: "John", email: "john@example.com" },
});
// 类型:User

const user = await api.request("/users/:id", "GET", {
   params: { id: "123" },
});
// 类型:User

Pattern 3: Builder Pattern with Type Safety

模式3:带类型安全的构建器模式

typescript
type BuilderState<T> = {
  [K in keyof T]: T[K] | undefined;
};

type RequiredKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];

type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

type IsComplete<T, S> =
  RequiredKeys<T> extends keyof S ? (S[RequiredKeys<T>] extends undefined ? false : true) : false;

class Builder<T, S extends BuilderState<T> = {}> {
  private state: S = {} as S;

  set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {
    this.state[key] = value;
    return this as any;
  }

  build(this: IsComplete<T, S> extends true ? this : never): T {
    return this.state as T;
  }
}

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

const builder = new Builder<User>();

const user = builder.set("id", "1").set("name", "John").set("email", "john@example.com").build(); // OK: all required fields set

// const incomplete = builder
//   .set("id", "1")
//   .build();  // Error: missing required fields
typescript
type BuilderState<T> = {
   [K in keyof T]: T[K] | undefined;
};

type RequiredKeys<T> = {
   [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];

type OptionalKeys<T> = {
   [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

type IsComplete<T, S> =
   RequiredKeys<T> extends keyof S ? (S[RequiredKeys<T>] extends undefined ? false : true) : false;

class Builder<T, S extends BuilderState<T> = {}> {
   private state: S = {} as S;

   set<K extends keyof T>(key: K, value: T[K]): Builder<T, S & Record<K, T[K]>> {
     this.state[key] = value;
     return this as any;
   }

   build(this: IsComplete<T, S> extends true ? this : never): T {
     return this.state as T;
   }
}

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

const builder = new Builder<User>();

const user = builder.set("id", "1").set("name", "John").set("email", "john@example.com").build(); // 合法:所有必填字段已设置

// const incomplete = builder
//   .set("id", "1")
//   .build();  // 错误:缺少必填字段

Pattern 4: Deep Readonly/Partial

模式4:深度只读/可选

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

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

interface Config {
  server: {
    host: string;
    port: number;
    ssl: {
      enabled: boolean;
      cert: string;
    };
  };
  database: {
    url: string;
    pool: {
      min: number;
      max: number;
    };
  };
}

type ReadonlyConfig = DeepReadonly<Config>;
// All nested properties are readonly

type PartialConfig = DeepPartial<Config>;
// All nested properties are optional
typescript
type DeepReadonly<T> = {
   readonly [P in keyof T]: T[P] extends object
     ? T[P] extends Function
       ? T[P]
       : DeepReadonly<T[P]>
     : T[P];
};

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

interface Config {
   server: {
     host: string;
     port: number;
     ssl: {
       enabled: boolean;
       cert: string;
     };
   };
   database: {
     url: string;
     pool: {
       min: number;
       max: number;
     };
   };
}

type ReadonlyConfig = DeepReadonly<Config>;
// 所有嵌套属性均为只读

type PartialConfig = DeepPartial<Config>;
// 所有嵌套属性均为可选

Pattern 5: Type-Safe Form Validation

模式5:类型安全的表单验证

typescript
type ValidationRule<T> = {
  validate: (value: T) => boolean;
  message: string;
};

type FieldValidation<T> = {
  [K in keyof T]?: ValidationRule<T[K]>[];
};

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

class FormValidator<T extends Record<string, any>> {
  constructor(private rules: FieldValidation<T>) {}

  validate(data: T): ValidationErrors<T> | null {
    const errors: ValidationErrors<T> = {};
    let hasErrors = false;

    for (const key in this.rules) {
      const fieldRules = this.rules[key];
      const value = data[key];

      if (fieldRules) {
        const fieldErrors: string[] = [];

        for (const rule of fieldRules) {
          if (!rule.validate(value)) {
            fieldErrors.push(rule.message);
          }
        }

        if (fieldErrors.length > 0) {
          errors[key] = fieldErrors;
          hasErrors = true;
        }
      }
    }

    return hasErrors ? errors : null;
  }
}

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

const validator = new FormValidator<LoginForm>({
  email: [
    {
      validate: v => v.includes("@"),
      message: "Email must contain @",
    },
    {
      validate: v => v.length > 0,
      message: "Email is required",
    },
  ],
  password: [
    {
      validate: v => v.length >= 8,
      message: "Password must be at least 8 characters",
    },
  ],
});

const errors = validator.validate({
  email: "invalid",
  password: "short",
});
// Type: { email?: string[]; password?: string[]; } | null
typescript
type ValidationRule<T> = {
   validate: (value: T) => boolean;
   message: string;
};

type FieldValidation<T> = {
   [K in keyof T]?: ValidationRule<T[K]>[];
};

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

class FormValidator<T extends Record<string, any>> {
   constructor(private rules: FieldValidation<T>) {}

   validate(data: T): ValidationErrors<T> | null {
     const errors: ValidationErrors<T> = {};
     let hasErrors = false;

     for (const key in this.rules) {
       const fieldRules = this.rules[key];
       const value = data[key];

       if (fieldRules) {
         const fieldErrors: string[] = [];

         for (const rule of fieldRules) {
           if (!rule.validate(value)) {
             fieldErrors.push(rule.message);
           }
         }

         if (fieldErrors.length > 0) {
           errors[key] = fieldErrors;
           hasErrors = true;
         }
       }
     }

     return hasErrors ? errors : null;
   }
}

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

const validator = new FormValidator<LoginForm>({
   email: [
     {
       validate: v => v.includes("@"),
       message: "邮箱必须包含@",
     },
     {
       validate: v => v.length > 0,
       message: "邮箱为必填项",
     },
   ],
   password: [
     {
       validate: v => v.length >= 8,
       message: "密码长度至少为8位",
     },
   ],
});

const errors = validator.validate({
   email: "invalid",
   password: "short",
});
// 类型:{ email?: string[]; password?: string[]; } | null

Pattern 6: Discriminated Unions

模式6:可辨识联合

typescript
type Success<T> = {
  status: "success";
  data: T;
};

type Error = {
  status: "error";
  error: string;
};

type Loading = {
  status: "loading";
};

type AsyncState<T> = Success<T> | Error | Loading;

function handleState<T>(state: AsyncState<T>): void {
  switch (state.status) {
    case "success":
      console.log(state.data); // Type: T
      break;
    case "error":
      console.log(state.error); // Type: string
      break;
    case "loading":
      console.log("Loading...");
      break;
  }
}

// Type-safe state machine
type State =
  | { type: "idle" }
  | { type: "fetching"; requestId: string }
  | { type: "success"; data: any }
  | { type: "error"; error: Error };

type Event =
  | { type: "FETCH"; requestId: string }
  | { type: "SUCCESS"; data: any }
  | { type: "ERROR"; error: Error }
  | { type: "RESET" };

function reducer(state: State, event: Event): State {
  switch (state.type) {
    case "idle":
      return event.type === "FETCH" ? { type: "fetching", requestId: event.requestId } : state;
    case "fetching":
      if (event.type === "SUCCESS") {
        return { type: "success", data: event.data };
      }
      if (event.type === "ERROR") {
        return { type: "error", error: event.error };
      }
      return state;
    case "success":
    case "error":
      return event.type === "RESET" ? { type: "idle" } : state;
  }
}
typescript
type Success<T> = {
   status: "success";
   data: T;
};

type Error = {
   status: "error";
   error: string;
};

type Loading = {
   status: "loading";
};

type AsyncState<T> = Success<T> | Error | Loading;

function handleState<T>(state: AsyncState<T>): void {
   switch (state.status) {
     case "success":
       console.log(state.data); // 类型:T
       break;
     case "error":
       console.log(state.error); // 类型:string
       break;
     case "loading":
       console.log("加载中...");
       break;
   }
}

// 类型安全的状态机
type State =
  | { type: "idle" }
  | { type: "fetching"; requestId: string }
  | { type: "success"; data: any }
  | { type: "error"; error: Error };

type Event =
  | { type: "FETCH"; requestId: string }
  | { type: "SUCCESS"; data: any }
  | { type: "ERROR"; error: Error }
  | { type: "RESET" };

function reducer(state: State, event: Event): State {
   switch (state.type) {
     case "idle":
       return event.type === "FETCH" ? { type: "fetching", requestId: event.requestId } : state;
     case "fetching":
       if (event.type === "SUCCESS") {
         return { type: "success", data: event.data };
       }
       if (event.type === "ERROR") {
         return { type: "error", error: event.error };
       }
       return state;
     case "success":
     case "error":
       return event.type === "RESET" ? { type: "idle" } : state;
   }
}

Type Inference Techniques

类型推断技巧

1. Infer Keyword

1. Infer关键字

typescript
// Extract array element type
type ElementType<T> = T extends (infer U)[] ? U : never;

type NumArray = number[];
type Num = ElementType<NumArray>; // number

// Extract promise type
type PromiseType<T> = T extends Promise<infer U> ? U : never;

type AsyncNum = PromiseType<Promise<number>>; // number

// Extract function parameters
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

function foo(a: string, b: number) {}
type FooParams = Parameters<typeof foo>; // [string, number]
typescript
// 提取数组元素类型
type ElementType<T> = T extends (infer U)[] ? U : never;

type NumArray = number[];
type Num = ElementType<NumArray>; // number

// 提取Promise类型
type PromiseType<T> = T extends Promise<infer U> ? U : never;

type AsyncNum = PromiseType<Promise<number>>; // number

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

function foo(a: string, b: number) {}
type FooParams = Parameters<typeof foo>; // [string, number]

2. Type Guards

2. 类型守卫

typescript
function isString(value: unknown): value is string {
  return typeof value === "string";
}

function isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[] {
  return Array.isArray(value) && value.every(guard);
}

const data: unknown = ["a", "b", "c"];

if (isArrayOf(data, isString)) {
  data.forEach(s => s.toUpperCase()); // Type: string[]
}
typescript
function isString(value: unknown): value is string {
   return typeof value === "string";
}

function isArrayOf<T>(value: unknown, guard: (item: unknown) => item is T): value is T[] {
   return Array.isArray(value) && value.every(guard);
}

const data: unknown = ["a", "b", "c"];

if (isArrayOf(data, isString)) {
   data.forEach(s => s.toUpperCase()); // 类型:string[]
}

3. Assertion Functions

3. 断言函数

typescript
function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== "string") {
    throw new Error("Not a string");
  }
}

function processValue(value: unknown) {
  assertIsString(value);
  // value is now typed as string
  console.log(value.toUpperCase());
}
typescript
function assertIsString(value: unknown): asserts value is string {
   if (typeof value !== "string") {
     throw new Error("不是字符串");
   }
}

function processValue(value: unknown) {
   assertIsString(value);
   // value现在被推断为string类型
   console.log(value.toUpperCase());
}

Best Practices

最佳实践

For detailed configuration and patterns, see references/best-practices.md.
有关详细的配置和模式,请查看 references/best-practices.md

Core Guidelines

核心指南

  1. Use
    unknown
    over
    any
    : Enforce type checking
  2. Prefer
    interface
    for object shapes
    : Better error messages, declaration merging
  3. Use
    type
    for unions and complex types
    : More flexible for computed types
  4. Leverage type inference: Let TypeScript infer when possible
  5. Create helper types: Build reusable type utilities
  6. Use const assertions: Preserve literal types
  7. Avoid type assertions: Use type guards instead
  8. Document complex types: Add JSDoc comments
  9. Use strict mode: Enable all strict compiler options
  10. Test your types: Use type tests to verify type behavior
  1. 使用
    unknown
    而非
    any
    :强制进行类型检查
  2. 优先用
    interface
    定义对象形状
    :错误信息更友好,支持声明合并
  3. type
    定义联合类型和复杂类型
    :对计算类型更灵活
  4. 利用类型推断:尽可能让TypeScript自动推断类型
  5. 创建辅助类型:构建可复用的类型工具
  6. 使用const断言:保留字面量类型
  7. 避免类型断言:改用类型守卫
  8. 为复杂类型添加文档:添加JSDoc注释
  9. 使用严格模式:启用所有严格编译选项
  10. 测试你的类型:使用类型测试验证类型行为

Type Strategy Summary

类型策略总结

ScenarioRecommendation
Function parametersAlways explicit
Return typesLet TypeScript infer (usually)
Local variablesLet TypeScript infer
Public API boundariesAlways explicit
Object shapes (extensible)Use
interface
Union typesUse
type
Computed/mapped typesUse
type
场景推荐方案
函数参数始终显式标注
返回类型通常让TypeScript自动推断
局部变量让TypeScript自动推断
公共API边界始终显式标注
可扩展的对象形状使用
interface
联合类型使用
type
计算/映射类型使用
type

Recommended tsconfig.json Settings

推荐的tsconfig.json设置

json
{
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "moduleResolution": "Bundler"
  }
}
json
{
  "compilerOptions": {
     "strict": true,
     "noUncheckedIndexedAccess": true,
     "exactOptionalPropertyTypes": true,
     "moduleResolution": "Bundler"
  }
}

Type Testing

类型测试

For detailed patterns, see references/best-practices.md.
typescript
// Type assertion tests - basic version
type AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;

// More robust equality check (handles edge cases better)
type Expect<T extends true> = T;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
  ? true
  : false;

// Usage examples
type Test1 = Expect<Equal<string, string>>; // passes
type Test2 = Expect<Equal<string, number>>; // fails at compile time

// Test type behavior at compile time
type TestReturnType = Expect<
  Equal<ReturnType<typeof myFunction>, ExpectedType>
>;

// Expect error helper
type ExpectError<T extends never> = T;

// Example usage
type ShouldError = ExpectError<AssertEqual<string, number>>;
有关详细模式,请查看 references/best-practices.md
typescript
// 类型断言测试 - 基础版本
type AssertEqual<T, U> = [T] extends [U] ? ([U] extends [T] ? true : false) : false;

// 更健壮的相等检查(更好地处理边缘情况)
type Expect<T extends true> = T;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2
  ? true
  : false;

// 使用示例
type Test1 = Expect<Equal<string, string>>; // 通过
type Test2 = Expect<Equal<string, number>>; // 编译时失败

// 在编译时测试类型行为
type TestReturnType = Expect<
  Equal<ReturnType<typeof myFunction>, ExpectedType>
>;

// 错误期望辅助工具
type ExpectError<T extends never> = T;

// 示例用法
type ShouldError = ExpectError<AssertEqual<string, number>>;

Common Pitfalls

常见陷阱

  1. Over-using
    any
    : Defeats the purpose of TypeScript
  2. Ignoring strict null checks: Can lead to runtime errors
  3. Too complex types: Can slow down compilation
  4. Not using discriminated unions: Misses type narrowing opportunities
  5. Forgetting readonly modifiers: Allows unintended mutations
  6. Circular type references: Can cause compiler errors
  7. Not handling edge cases: Like empty arrays or null values
  1. 过度使用
    any
    :违背TypeScript的设计初衷
  2. 忽略严格空检查:可能导致运行时错误
  3. 类型过于复杂:会减慢编译速度
  4. 未使用可辨识联合:错失类型收窄的机会
  5. 忘记readonly修饰符:允许意外的突变
  6. 循环类型引用:可能导致编译器错误
  7. 未处理边缘情况:比如空数组或null值

Performance Considerations

性能考量

For detailed examples, see references/best-practices.md.
  • Avoid deeply nested conditional types
  • Use simple types when possible
  • Cache complex type computations
  • Limit recursion depth in recursive types
  • Use build tools to skip type checking in production
  • Prefer type guards over type assertions for runtime safety
  • Keep union types small and simple in hot paths
有关详细示例,请查看 references/best-practices.md
  • 避免深度嵌套的条件类型
  • 尽可能使用简单类型
  • 缓存复杂的类型计算
  • 限制递归类型的递归深度
  • 使用构建工具在生产环境中跳过类型检查
  • 优先使用类型守卫而非类型断言以保证运行时安全
  • 在热点路径中保持联合类型小而简单

Resources

资源