typescript-advanced-types

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

TypeScript Advanced Types

TypeScript高级类型

Overview

概述

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高级类型系统的全面指南,涵盖泛型、条件类型、映射类型、模板字面量类型和工具类型,助力构建健壮、类型安全的应用程序。

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: Deep Readonly/Partial

模式2:深度只读/可选

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;
    };
  };
}

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;
    };
  };
}

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

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

Pattern 3: Discriminated Unions

模式3:可辨识联合类型

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;
  }
}
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("Loading...");
      break;
  }
}

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("Not a string");
  }
}

function processValue(value: unknown) {
  assertIsString(value);
  // 此时value类型为string
  console.log(value.toUpperCase());
}

Quick Reference

速查手册

UtilityPurpose
Partial<T>
All properties optional
Required<T>
All properties required
Readonly<T>
All properties readonly
Pick<T, K>
Select properties
Omit<T, K>
Remove properties
Exclude<T, U>
Remove from union
Extract<T, U>
Keep from union
NonNullable<T>
Remove null/undefined
Record<K, T>
Object with keys K, values T
ReturnType<T>
Function return type
Parameters<T>
Function parameter types
工具类型用途
Partial<T>
将所有属性设为可选
Required<T>
将所有属性设为必填
Readonly<T>
将所有属性设为只读
Pick<T, K>
选择指定属性
Omit<T, K>
移除指定属性
Exclude<T, U>
从联合类型中移除指定类型
Extract<T, U>
从联合类型中提取指定类型
NonNullable<T>
排除null和undefined
Record<K, T>
创建键为K、值为T的对象类型
ReturnType<T>
获取函数返回类型
Parameters<T>
获取函数参数类型

Best Practices

最佳实践

Do

建议

  • Use
    unknown
    over
    any
    - enforce type checking
  • Prefer
    interface
    for object shapes - better error messages
  • Use
    type
    for unions and complex types
  • Leverage type inference - let TypeScript infer when possible
  • Create helper types - build reusable utilities
  • Use const assertions - preserve literal types
  • Use type guards instead of assertions
  • Enable strict mode in tsconfig
  • 使用
    unknown
    替代
    any
    - 强制类型检查
  • 优先用
    interface
    定义对象结构 - 错误提示更友好
  • type
    定义联合类型和复杂类型
  • 充分利用类型推断 - 让TypeScript自动推导类型
  • 创建辅助类型 - 构建可复用的工具类型
  • 使用const断言 - 保留字面量类型
  • 使用类型守卫而非类型断言
  • 在tsconfig中启用严格模式

Don't

避免

  • Over-use
    any
    - defeats TypeScript's purpose
  • Ignore strict null checks - leads to runtime errors
  • Create overly complex types - slows compilation
  • Forget readonly modifiers - allows unintended mutations
  • Use type assertions when guards work - loses type safety
  • 过度使用
    any
    - 违背TypeScript设计初衷
  • 忽略严格空检查 - 导致运行时错误
  • 创建过于复杂的类型 - 减慢编译速度
  • 忘记使用readonly修饰符 - 允许意外的属性修改
  • 能用类型守卫时使用类型断言 - 丢失类型安全性

Common Pitfalls

参考资源

PitfallSolution
Circular type referencesBreak cycle with explicit types
Deeply nested conditionalsSimplify or use helper types
Missing discriminantAdd
type
or
kind
field
Forgetting
infer
scope
Use inside
extends
clause
Union distribution unexpectedWrap in tuple
[T]

Resources