前端工程Technical Deep Dive

TypeScript 高级类型 —— 泛型、条件类型、映射类型完全指南

发布时间2026/03/29
分类前端工程
预计阅读2 分钟
作者吴长龙
*

TypeScript 类型系统概述

01.内容

TypeScript 类型系统概述

TypeScript 的类型系统是其核心优势,从基础类型到高级类型,形成了一套强大的类型编程能力。

一、泛型(Generics)

#### 基本泛型

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

const str = identity('hello');  // type: string
const num = identity(123);       // type: number

// 泛型接口
interface Container<T> {
  value: T;
  getValue(): T;
}

const container: Container<string> = {
  value: 'hello',
  getValue() { return this.value; }
};

#### 泛型约束

typescript snippettypescript
// 约束为特定类型
interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): void {
  console.log(arg.length);
}

logLength('hello');     // OK
logLength([1, 2, 3]);   // OK
logLength({ length: 5 }); // OK
// logLength(123);      // Error

// 约束为另一个类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const obj = { a: 1, b: 'hello' };
getProperty(obj, 'a');  // type: number
getProperty(obj, 'b');  // type: string
// getProperty(obj, 'c'); // Error

#### 泛型类

typescript snippettypescript
class Stack<T> {
  private items: T[] = [];
  
  push(item: T): void {
    this.items.push(item);
  }
  
  pop(): T | undefined {
    return this.items.pop();
  }
}

const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
const num = numberStack.pop();  // type: number | undefined

二、条件类型

#### 基础条件类型

typescript snippettypescript
type IsString<T> = T extends string ? true : false;

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

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

type Elem = ArrayElement<string[]>;  // string
type Elem2 = ArrayElement<number[]>; // number

#### 高级用法:分布式条件类型

typescript snippettypescript
type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArr = ToArray<string | number>;
// 等价于: string[] | number[]

// 实际应用:排除 null/undefined
type NonNullable<T> = T extends null | undefined ? never : T;

type A = NonNullable<string | null | undefined>;  // string

#### 预定义条件类型

typescript snippettypescript
// Extract: 提取可赋值的类型
type Extract<T, U> = T extends U ? T : never;

type T = Extract<string | number, string>;  // string

// Exclude: 排除可赋值的类型
type Exclude<T, U> = T extends U ? never : T;

type T2 = Exclude<string | number, string>;  // number

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

function getData() { return { id: 1 }; }
type Data = ReturnType<typeof getData>;  // { id: number }

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

function handleClick(id: string, name: string) {}
type Params = Parameters<typeof handleClick>;  // [string, string]

三、映射类型

#### 基础映射

typescript snippettypescript
type Props = {
  id: number;
  name: string;
  email: string;
};

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

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

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

type ReadonlyProps = Readonly<Props>;
// { readonly id: number; readonly name: string; ... }

#### 泛型映射

typescript snippettypescript
// pick: 选择部分属性
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

type Picked = Pick<Props, 'id' | 'name'>;
 // { id: number; name: string; }

// omit: 排除部分属性
type Omit<T, K extends keyof T> = {
  [P in Exclude<keyof T, K>]: T[P];
};

type Omitted = Omit<Props, 'email'>;
 // { id: number; name: string; }

四、模板字面量类型

typescript snippettypescript
// 基础用法
type EventName = `on${string}`;
type Handler = `handle${string}`;

const e: EventName = 'onClick';    // OK
const h: Handler = 'handleSubmit'; // OK

// 提取部分
type Color = 'red' | 'green' | 'blue';
type ColorProp = `color-${Color}`;
// "color-red" | "color-green" | "color-blue"

// 推断
type Route = '/users' | '/posts' | '/comments';

type ExtractRouteParams<T> = T extends `${string}:${infer P}/${infer Rest}` 
  ? P | ExtractRouteParams<`/${Rest}`>
  : T extends `${string}:${infer P}` 
    ? P 
    : never;

// 实际应用
type UserRoute = '/users/:id/posts/:postId';
type Params = ExtractRouteParams<UserRoute>;
// "id" | "postId"

五、实战:类型工具库

#### 1. 深 Partial

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

type Nested = {
  user: {
    profile: {
      name: string;
      age: number;
    };
  };
};

type DeepNested = DeepPartial<Nested>;
// { user?: { profile?: { name?: string; age?: number; } } }

#### 2. 严格类型

typescript snippettypescript
type StrictOmit<T, K extends keyof T> = {
  [P in Exclude<keyof T, K>]: T[P];
};

type StrictPick<T, K extends keyof T> = {
  [P in K]: T[P];
};

// 与内置的区别:严格模式下会报错

#### 3. 函数类型增强

typescript snippettypescript
// 异步函数返回类型
type AsyncReturnType<T extends (...args: any) => Promise<any>> =
  T extends (...args: any) => Promise<infer R> ? R : any;

// 组合函数
type Compose<T> = (arg: any) => T;

function pipe<T>(...fns: Array<(arg: any) => any>) {
  return (input: any) => fns.reduce((acc, fn) => fn(acc), input);
}

// 用法
const process = pipe(
  (x: number) => x + 1,
  (x: number) => x * 2,
  (x: number) => String(x)
);
// process: (input: number) => string

六、类型守卫

typescript snippettypescript
// typeof 守卫
function padLeft(value: string | number, padding: string | number) {
  if (typeof padding === 'number') {
    return Array(padding + 1).join(' ') + value;
  }
  if (typeof padding === 'string') {
    return padding + value;
  }
  throw new Error('Expected string or number');
}

// in 守卫
interface Fish { swim(): void; }
interface Bird { fly(): void; }

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    return animal.swim();
  }
  return animal.fly();
}

// 类型断言函数
function isString(value: any): value is string {
  return typeof value === 'string';
}

function example(x: string | number) {
  if (isString(x)) {
    return x.toUpperCase(); // type: string
  }
  return x.toFixed(2); // type: number
}

七、实战案例

#### 1. API 响应类型

typescript snippettypescript
type ApiResponse<T> = 
  | { success: true; data: T }
  | { success: false; error: string };

async function fetchUser(id: string): Promise<ApiResponse<User>> {
  try {
    const user = await getUser(id);
    return { success: true, data: user };
  } catch (e) {
    return { success: false, error: e.message };
  }
}

// 使用时自动推断
const result = await fetchUser('1');
if (result.success) {
  console.log(result.data.name);  // User 类型
} else {
  console.error(result.error);    // string 类型
}

#### 2. 事件处理类型

typescript snippettypescript
type EventMap = {
  click: { x: number; y: number };
  focus: { target: HTMLElement };
  input: { value: string };
};

type EventCallback<T> = (data: T) => void;

class EventEmitter<T extends Record<string, any>> {
  private listeners: {
    [K in keyof T]?: Array<EventCallback<T[K]>>;
  } = {};
  
  on<K extends keyof T>(event: K, callback: EventCallback<T[K]>): void {
    this.listeners[event] = (this.listeners[event] || []).concat(callback);
  }
  
  emit<K extends keyof T>(event: K, data: T[K]): void {
    this.listeners[event]?.forEach(cb => cb(data));
  }
}

const emitter = new EventEmitter<EventMap>();
emitter.on('click', (data) => {
  console.log(data.x, data.y);  // 类型安全
});
emitter.emit('click', { x: 10, y: 20 });

总结

TypeScript 高级类型是构建类型安全应用的基础:

技术用途
泛型代码复用、类型参数化
条件类型类型推导、类型过滤
映射类型类型转换、属性修改
模板字面量字符串类型构造
类型守卫运行时类型检查

熟练掌握这些技术,能写出更安全、更可维护的代码。