TypeScript 高阶用法全解析
一、核心要点速览
💡 核心考点
- 泛型进阶:泛型约束、默认参数、泛型工具函数
- 条件类型:infer 推断、分布式条件类型
- 映射类型:in 操作符、as 重映射、修饰符
- 工具类型:Pick、Omit、Partial、Required 等实现原理
- 类型体操:常用类型技巧与实战应用
二、泛型进阶
1. 泛型约束 (Generic Constraints)
typescript
// 基础约束:extends 关键字
interface Lengthwise {
length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // ✅ 可以访问 length
return arg;
}
loggingIdentity("hello"); // ✅ string 有 length
loggingIdentity([1, 2, 3]); // ✅ array 有 length
loggingIdentity(123); // ❌ number 没有 length多约束条件:
typescript
// 多个约束的联合类型
function merge<T extends object, U extends object>(
objA: T,
objB: U
): T & U {
return Object.assign(objA, objB);
}
const result = merge({ name: "Alice" }, { age: 25 });
// 类型:{ name: string } & { age: number }使用 keyof 约束:
typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "John", age: 30 };
getProperty(person, "name"); // ✅
getProperty(person, "email"); // ❌ 'email' 不在 keyof T 中2. 泛型默认参数
typescript
interface Response<T = any> {
data: T;
status: number;
message: string;
}
// 使用默认值
const res1: Response = { data: null, status: 200, message: "OK" };
// 指定具体类型
interface ApiResponse<T = unknown> {
code: number;
result: T;
}
type UserResponse = ApiResponse<{ id: number; name: string }>;3. 泛型工具函数
typescript
// 泛型数组操作
function filterTruthy<T>(arr: (T | null | undefined)[]): T[] {
return arr.filter((item): item is T => item != null);
}
filterTruthy([1, null, 2, undefined, 3]); // [1, 2, 3]
// 泛型 Promise 处理
function unwrapPromise<T>(promise: Promise<T>): Promise<T> {
return promise.then((res) => res);
}
// 泛型事件处理
class EventEmitter<T extends Record<string, any>> {
private listeners: {
[K in keyof T]?: ((data: T[K]) => void)[];
} = {};
on<K extends keyof T>(event: K, callback: (data: T[K]) => void) {
this.listeners[event]?.push(callback);
}
emit<K extends keyof T>(event: K, data: T[K]) {
this.listeners[event]?.forEach((cb) => cb(data));
}
}
// 使用
const emitter = new EventEmitter<{
login: { userId: number };
logout: void;
}>();
emitter.on("login", (data) => console.log(data.userId));三、条件类型 (Conditional Types)
1. 基础语法
typescript
// T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<123>; // false2. infer 关键字 - 类型推断
typescript
// 推断函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type FnReturn = ReturnType<() => string>; // string
// 推断数组元素类型
type ElementType<T> = T extends (infer U)[] ? U : T;
type ArrElem = ElementType<string[]>; // string
// 推断 Promise 内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type PromiseValue = UnwrapPromise<Promise<number>>; // number
// 推断函数参数类型
type FirstParameter<T> = T extends (
arg1: infer P,
...args: any[]
) => any
? P
: never;
type Param = FirstParameter<(name: string, age: number) => void>; // string3. 分布式条件类型
typescript
// 当 T 是联合类型时,条件类型会分发到每个成员
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>;
// 等价于:ToArray<string> | ToArray<number>
// 结果:string[] | number[]
// 实际应用:提取联合类型的字面量
type GetValues<T> = T extends { value: infer V } ? V : never;
type Options =
| { type: "text"; value: string }
| { type: "number"; value: number }
| { type: "boolean"; value: boolean };
type ValueTypes = GetValues<Options>; // string | number | boolean4. 实用条件类型示例
typescript
// 判断是否为 any
type IsAny<T> = 0 extends 1 & T ? true : false;
type A = IsAny<any>; // true
type B = IsAny<string>; // false
// 判断是否为 never
type IsNever<T> = [T] extends [never] ? true : false;
type C = IsNever<never>; // true
type D = IsNever<string>; // false
// 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;
type E = NonNullable<string | null | undefined>; // string
// 提取函数的参数类型
type Parameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never;
type FnParams = Parameters<(name: string, age: number) => void>;
// [string, number]四、映射类型 (Mapped Types)
1. 基础映射类型
typescript
// 将属性变为可选
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;
}
type PartialUser = Partial<User>; // { name?: string; age?: number }
type RequiredUser = Required<User>; // { name: string; age: number }2. 键的重映射 (Key Remapping)
typescript
// 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 PropertiesWithoutId<T> = {
[K in keyof T as Exclude<K, "id">]: T[K];
};
interface Entity {
id: number;
name: string;
email: string;
}
type EntityWithoutId = PropertiesWithoutId<Entity>;
// { name: string; email: string }
// 只保留特定类型
type ExtractStringKeys<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface Mixed {
name: string;
age: number;
email: string;
active: boolean;
}
type StringOnly = ExtractStringKeys<Mixed>;
// { name: string; email: string }3. 值类型转换
typescript
// 将所有属性转为字符串
type Stringify<T> = {
[K in keyof T]: string;
};
type UserString = Stringify<{ name: string; age: number }>;
// { name: string; age: string }
// 嵌套对象的深度字符串化
type DeepStringify<T> = {
[K in keyof T]: T[K] extends object
? DeepStringify<T[K]>
: string;
};
interface Nested {
user: {
name: string;
profile: {
age: number;
};
};
status: boolean;
}
type DeepString = DeepStringify<Nested>;
// { user: { name: string; profile: { age: string } }; status: string }4. 组合映射类型
typescript
// 组合 Partial 和 Readonly
type ReadonlyPartial<T> = {
readonly [P in keyof T]?: T[P];
};
// 组合 Pick 和 Readonly
type ReadonlyPick<T, K extends keyof T> = {
readonly [P in K]: T[P];
};
// 实际应用:不可变的更新函数参数
type ImmutableUpdate<T> = {
readonly [P in keyof T]?: T[P] | ((prev: T[P]) => T[P]);
};
function updateState<T>(
state: T,
updates: ImmutableUpdate<T>
): T {
const result = { ...state };
for (const key in updates) {
const value = updates[key];
if (typeof value === "function") {
result[key] = value(result[key]);
} else {
result[key] = value;
}
}
return result;
}五、内置工具类型详解
1. 属性操作类
typescript
// Pick - 选择部分属性
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
interface Todo {
title: string;
description: string;
completed: boolean;
}
type TodoPreview = Pick<Todo, "title" | "completed">;
// { title: string; completed: boolean }
// Omit - 排除部分属性
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type TodoInfo = Omit<Todo, "completed">;
// { title: string; description: string }
// Extract - 提取匹配的类型
type Extract<T, U> = T extends U ? T : never;
type ValidEvents = Extract<"click" | "scroll" | "error", "click" | "scroll">;
// "click" | "scroll"
// Exclude - 排除匹配的类型
type Exclude<T, U> = T extends U ? never : T;
type RemainingEvents = Exclude<"click" | "scroll" | "error", "click">;
// "scroll" | "error"2. 可空性操作类
typescript
// Partial - 全部可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
// Required - 全部必填
type Required<T> = {
[P in keyof T]-?: T[P];
};
// Readonly - 全部只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// Mutable - 移除 readonly
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
};
// ReadonlyArray - 只读数组
type ReadonlyArray<T> = readonly T[];
// 示例
interface Config {
debug: boolean;
timeout: number;
}
type OptionalConfig = Partial<Config>; // { debug?: boolean; timeout?: number }
type StrictConfig = Required<Config>; // { debug: boolean; timeout: number }3. 函数相关类
typescript
// Parameters - 获取参数类型
type Parameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never;
type FnParams = Parameters<(a: string, b: number) => void>;
// [string, number]
// ConstructorParameters - 获取构造函数参数
type ConstructorParameters<
T extends abstract new (...args: any[]) => any
> = T extends abstract new (...args: infer P) => any ? P : never;
class MyClass {
constructor(public name: string, public age: number) {}
}
type ClassParams = ConstructorParameters<typeof MyClass>;
// [string, number]
// ReturnType - 获取返回类型
type ReturnType<T extends (...args: any[]) => any> =
T extends (...args: any[]) => infer R ? R : any;
type FnReturn = ReturnType<() => Promise<string>>;
// Promise<string>
// InstanceType - 获取实例类型
type InstanceType<T extends abstract new (...args: any[]) => any> =
T extends abstract new (...args: any[]) => infer R ? R : any;
type MyInstance = InstanceType<typeof MyClass>;
// MyClass
// ThisParameterType - 获取 this 类型
type ThisParameterType<T> = T extends (this: infer U, ...args: any[]) => any
? U
: unknown;
// OmitThisParameter - 移除 this 参数
type OmitThisParameter<T> = unknown extends ThisParameterType<T>
? T
: T extends (...args: infer A) => infer R
? (...args: A) => R
: T;4. 其他工具类型
typescript
// Record - 构建对象类型
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type PageInfo = Record<"home" | "about" | "contact", { title: string }>;
// { home: { title: string }; about: { title: string }; contact: { title: string } }
// NonNullable - 排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;
type A = NonNullable<string | null | undefined>; // string
// Capitalize/Uncapitalize - 首字母大小写转换
type Greeting = Capitalize<"hello">; // "Hello"
type LowerGreeting = Uncapitalize<"Hello">; // "hello"
// Awaited - Promise 解包
type PromiseResult = Awaited<Promise<string>>; // string
type NestedPromise = Awaited<Promise<Promise<number>>>; // number六、类型体操实战
1. 字符串操作类型
typescript
// 字符串拼接
type Concat<T extends string, U extends string> = `${T}${U}`;
type Result = Concat<"Hello", "World">; // "HelloWorld"
// 转大写
type Uppercase<T extends string> = /* intrinsic */ T;
// 自定义模板类型
type EventName<T extends string> = `on${Capitalize<T>}Change`;
type NameChange = EventName<"user">; // "onUserChange"
// 分割字符串
type Split<S extends string, D extends string> =
S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S];
type Parts = Split<"a-b-c", "-">; // ["a", "b", "c"]
// 连接数组
type Join<T extends string[], D extends string> =
T extends [infer F extends string, ...infer R extends string[]]
? R extends []
? F
: `${F}${D}${Join<R, D>}`
: never;
type Path = Join<["api", "users", "123"], "/">; // "api/users/123"2. 数字操作类型
typescript
// 递增
type Increment<N extends number> =
[...TupleOf<N>, 1]["length"] & number;
type TupleOf<N extends number, T extends any[] = []> =
T["length"] extends N ? T : TupleOf<N, [...T, any]>;
type Next = Increment<5>; // 6
// 递减(简化版)
type Decrement<N extends number> =
N extends 0 ? 0 :
[...TupleOf<N>]["length"] & number;
// 加法
type Add<A extends number, B extends number> =
[...TupleOf<A>, ...TupleOf<B>]["length"] & number;
type Sum = Add<3, 5>; // 83. 数组操作类型
typescript
// 第一个元素
type First<T extends any[]> = T extends [infer F, ...any[]] ? F : never;
type Head = First<[string, number, boolean]>; // string
// 最后一个元素
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
type Tail = Last<[string, number, boolean]>; // boolean
// 移除第一个
type DropFirst<T extends any[]> = T extends [any, ...infer R] ? R : never;
type Rest = DropFirst<[string, number, boolean]>; // [number, boolean]
// 移除最后一个
type DropLast<T extends any[]> = T extends [...infer I, any] ? I : never;
type Init = DropLast<[string, number, boolean]>; // [string, number]
// 数组长度的类型
type Length<T extends readonly any[]> = T["length"];
type Len = Length<[1, 2, 3]>; // 34. 对象深度操作
typescript
// 深度 Partial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
interface Nested {
user: {
name: string;
profile: {
age: number;
};
};
}
type DeepOpt = DeepPartial<Nested>;
// { user?: { name?: string; profile?: { age?: number } } }
// 深度 Readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 按路径获取类型
type GetPath<T, P extends string> =
P extends `${infer K}.${infer R}`
? K extends keyof T
? GetPath<T[K], R>
: never
: P extends keyof T
? T[P]
: never;
interface Config {
database: {
host: string;
port: number;
};
}
type Host = GetPath<Config, "database.host">; // string5. 函数柯里化类型
typescript
// 柯里化函数类型
type Curry<T extends (...args: any[]) => any> =
T extends (arg: infer A) => infer R
? (arg: A) => Curry<R>
: ReturnType<T>;
declare function add(a: number): (b: number) => (c: number) => number;
type CurriedAdd = Curry<typeof add>;
// (a: number) => (b: number) => (c: number) => number
// 参数反转
type ReverseParams<T extends any[]> =
T extends [infer F, ...infer R]
? [...ReverseParams<R>, F]
: [];
type Reversed = ReverseParams<[string, number, boolean]>;
// [boolean, number, string]七、高级模式与技巧
1. 标签模板类型
typescript
// 类型安全的 API 调用
type ApiPaths = `/api/${string}`;
function fetchApi<T>(path: ApiPaths): Promise<T> {
return fetch(path).then(res => res.json());
}
// ✅ 安全
fetchApi<{ users: string[] }>("/api/users");
// ❌ 错误
fetchApi("/users");2. 品牌类型 (Branded Types)
typescript
// 创建品牌类型
type UserId = string & { readonly brand: unique symbol };
type Email = string & { readonly brand: unique symbol };
function createUserId(id: string): UserId {
return id as UserId;
}
function validateEmail(email: string): Email {
if (!email.includes("@")) throw new Error("Invalid email");
return email as Email;
}
// 类型安全
const userId: UserId = createUserId("123");
const email: Email = validateEmail("[email protected]");
// ❌ 不能混用
function sendEmail(to: Email) {}
sendEmail(userId); // 类型错误3. 精确对象类型
typescript
// 禁止多余属性
type Exact<T, Shape extends T> = T &
Record<Exclude<keyof Shape, keyof T>, never>;
function processUser(user: Exact<{ name: string }, { name: string; age?: number }>) {
return user;
}
processUser({ name: "John" }); // ✅
processUser({ name: "John", age: 30 }); // ✅
processUser({ name: "John", extra: "field" }); // ❌ 类型错误4. 条件推断的高级应用
typescript
// 自动推断 React Hook 类型
type HookReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 推断组件 Props
type ComponentProps<T> = T extends React.ComponentType<infer P> ? P : never;
// 推断 Zod Schema 类型
type InferZodType<T> = T extends z.ZodType<infer U> ? U : never;5. 递归类型限制解决方案
typescript
// TypeScript 限制递归深度为 50
// 使用尾递归优化
// ❌ 可能导致递归过深
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;
// ✅ 使用辅助类型
type FlattenHelper<T, Acc extends any[] = []> =
T extends [infer F, ...infer R]
? FlattenHelper<R, [...Acc, Flatten<F>]>
: Acc;
type Flatten<T> = FlattenHelper<T>[number];八、实战应用场景
1. API 响应类型处理
typescript
// 统一的 API 响应包装
type ApiResponse<T> = {
code: number;
data: T;
message: string;
};
// 提取数据类型
type Data<T> = T extends ApiResponse<infer U> ? U : never;
// 处理分页响应
type PaginatedResponse<T> = ApiResponse<{
items: T[];
total: number;
page: number;
}>;
// 使用
type UserList = PaginatedResponse<{ id: number; name: string }>;2. 表单验证类型
typescript
// 验证规则定义
type ValidationRule<T> = {
required?: boolean;
pattern?: RegExp;
validator?: (value: T) => boolean;
message?: string;
};
// 表单配置类型
type FormConfig<T> = {
[K in keyof T]: ValidationRule<T[K]>;
};
// 表单数据状态
type FormState<T> = {
values: T;
errors: Partial<Record<keyof T, string>>;
touched: Partial<Record<keyof T, boolean>>;
};3. Redux/Vuex 类型增强
typescript
// Action 类型定义
type Action<T extends string, P = void> = {
type: T;
payload: P;
};
// Reducer 类型
type Reducer<S, A extends { type: string }> =
(state: S, action: A) => S;
// Store 类型
type Store<S, A extends { type: string }> = {
state: S;
dispatch: (action: A) => void;
subscribe: (listener: () => void) => () => void;
};
// 使用示例
type UserAction =
| Action<"LOGIN", { username: string }>
| Action<"LOGOUT">;
type UserState = { isLoggedIn: boolean; username?: string };4. 数据库模型映射
typescript
// Prisma 风格类型
type ModelFields = {
id: { type: "Int"; primary: true; autoincrement: true };
name: { type: "String"; nullable: false };
email: { type: "String"; nullable: true };
};
// 从 Schema 推断类型
type InferModel<T extends Record<string, ModelFields>> = {
[K in keyof T]: T[K]["nullable"] extends true
? T[K]["type"] extends "Int" ? number | null
: T[K]["type"] extends "String" ? string | null
: never
: T[K]["type"] extends "Int" ? number
: T[K]["type"] extends "String" ? string
: never;
};
type User = InferModel<{
id: { type: "Int"; primary: true; autoincrement: true };
name: { type: "String"; nullable: false };
email: { type: "String"; nullable: true };
}>;
// { id: number; name: string; email: string | null }九、常见面试题
Q1: 解释一下 infer 关键字的作用?
回答要点:
infer用于条件类型中的类型推断- 通常在
extends语句的条件分支中使用 - 可以提取函数返回值、数组元素、Promise 内部类型等
- 示例:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any
Q2: 什么是分布式条件类型?有什么特点?
回答要点:
- 当条件类型的检查对象是联合类型时,会自动分发到每个成员
- 形式:
T extends U ? X : Y,其中 T 是裸类型参数 - 特点:分别对联合类型每个成员应用条件,最后合并结果
- 用途:批量类型转换、过滤等
Q3: 如何实现一个 Omit 类型?
回答要点:
typescript
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>- 使用
Exclude从 T 的键中排除 K - 使用
Pick选择剩余的键 - 核心是组合使用工具类型
Q4: 映射类型中的 as 有什么用?
回答要点:
- TypeScript 4.1+ 引入的键重映射功能
- 可以在映射时修改键名
- 示例:
[K in keyof T as \get${K}`]: T[K]` - 可以配合
extends进行条件映射
Q5: 如何编写类型安全的 API 客户端?
回答要点:
- 使用模板字符串类型约束路径
- 使用泛型定义响应类型
- 使用条件类型推断返回数据
- 使用品牌类型防止类型混淆
- 示例代码展示完整实现
十、记忆口诀
📝 快速记忆
TypeScript 高阶口诀:
- 泛型约束用 extends,infer 推断藏条件
- 映射类型用 in 遍历,as 重映射改键名
- 条件分发联合类型,工具类型常组合
- Pick Omit 互为实现,Partial Required 相反用
- 类型体操练推理,实战应用更熟练
十一、延伸学习
推荐资源
- TypeScript 官方手册 - 高级类型
- Type Challenges - 类型体操练习
- Utility Types - 工具类型大全
- Total TypeScript - 深入学习资源
下一步建议
- 实践 Type Challenges 提升类型推理能力
- 学习 Vue 3 源码中的类型技巧
- 了解 React Hooks 类型模式