2026.01.01
TypeScript 高级类型详解
深入理解 TypeScript 的高级类型操作,掌握类型编程的核心概念和最佳实践
类型查询与操作
typeof 类型推导
typeof 操作符可以在类型上下文中使用,用于获取值的类型信息。这是 TypeScript 类型系统中非常实用的特性。
// 基础类型推导
let temp1 = 'hello world'
const temp2 = null
const temp3 = (a: string) => a.toUpperCase()
// 对象类型推导
const user = {
name: '小明',
age: 18,
address: {
province: '北京',
city: '昌平'
}
}
// 类型查询
type Temp1 = typeof temp1 // string
type Temp2 = typeof temp2 // null
type Temp3 = typeof temp3 // (a: string) => string
type User = typeof user // 完整的对象类型
type UserName = typeof user.name // string
💡 tips:
typeof在类型位置使用时,推导的是编译时类型,而非运行时值。
索引访问类型 TK
使用索引访问类型可以精确获取对象类型中特定 key 对应的类型。
interface User {
age: number
name: string
id: string
}
// 基础访问
type Age = User['age'] // number
type Name = User['name'] // string
// 联合类型访问
type AgeOrName = User['age' | 'name'] // string | number
// 数组元素类型访问
const colors = ['red', 'green', 'blue']
type Color = typeof colors[number] // string
// 配合 as const 使用
const roles = ['Admin', 'User', 'Guest'] as const
type Role = typeof roles[number] // 'Admin' | 'User' | 'Guest'
// 嵌套访问
interface NestedObj {
user: {
name: string
age: number
}
settings: {
theme: 'light' | 'dark'
language: string
}
}
type UserName = NestedObj['user']['name'] // string
type Theme = NestedObj['settings']['theme'] // 'light' | 'dark'
💡 tips:使用
as const可以将数组转换为具体的字面量联合类型,实现更严格的类型控制。
键值操作
keyof 操作符
keyof 是类型系统的"反射"操作符,可以获取类型所有键的联合类型。
interface User {
id: number
name: string
age: number
}
// 获取所有键的联合类型
type UserKeys = keyof User // "id" | "name" | "age"
// 结合索引访问获取所有值的联合类型
type UserValues = User[keyof User] // string | number
// 实际应用
type RequiredKey = keyof User // 必填字段
type OptionalKey = keyof Partial<User> // 可选字段
in 操作符
in 操作符用于映射类型,可以遍历联合类型的每个成员。
// 基础用法
type Keys = 'name' | 'age' | 'id'
// 创建映射类型
type User = {
[key in Keys]: string
}
// type User = {
// name: string;
// age: string;
// id: string;
// }
// 带条件映射
type OptionalUser = {
[key in keyof User]?: User[key]
}
// type OptionalUser = {
// name?: string;
// age?: string;
// id?: string;
// }
索引签名
索引签名允许定义对象可以包含任意数量的属性。
// 基础索引签名
interface AnyObject {
[key: string]: any
}
// 限制值类型
interface Config {
[key: string]: string | number | boolean
}
// 键值类型受限
interface StrictConfig {
[key: 'theme' | 'language']: string
}
// 使用示例
const user: Config = {
name: '小明',
age: 18,
isActive: true
// 可以动态添加新属性
}
⚠️ 使用索引签名时,所有显式声明的属性类型必须兼容索引签名的值类型。
索引签名 vs Record
Record<K, T> 是 TypeScript 内置的工具类型,作用类似于索引签名,但更简洁。
// 索引签名方式
interface User1 {
[key: string]: string
}
// Record 方式
type User2 = Record<string, string>
// 实际应用场景
type Roles = Record<'admin' | 'user' | 'guest', string[]>
const rolePermissions: Roles = {
admin: ['read', 'write', 'delete'],
user: ['read'],
guest: []
}
条件类型
条件类型是 TypeScript 的"三元运算符",允许基于类型条件返回不同的类型。
基础语法
// 基础条件类型
type IsString<T> = T extends string ? true : false
type Test1 = IsString<'hello'> // true
type Test2 = IsString<number> // false
type Test3 = IsString<string> // true
分布式条件类型
当条件类型与联合类型结合使用时,会触发分布式行为。
type ExtractString<T> = T extends string ? T : never
type Union = 'a' | 'b' | 'c'
type StringsOnly = ExtractString<Union> // 'a' | 'b' | 'c'
// 工作原理相当于:
// 'a' extends string ? 'a' : never // 'a'
// 'b' extends string ? 'b' : never // 'b'
// 'c' extends string ? 'c' : never // 'c'
// 最终结果:'a' | 'b' | 'c'
实用工具类型实现
// 1. Extract - 提取联合类型中的部分
type MyExtract<T, U> = T extends U ? T : never
type A = 'a' | 'b' | 'c'
type B = 'a' | 'c'
type C = MyExtract<A, B> // 'a' | 'c'
// 2. Exclude - 排除联合类型中的部分
type MyExclude<T, U> = T extends U ? never : T
type D = MyExclude<A, B> // 'b'
// 3. NonNullable - 排除 null 和 undefined
type MyNonNullable<T> = T extends null | undefined ? never : T
type E = MyNonNullable<string | null | number> // string | number
高级应用 - 类型合并
// 合并两个类型,后面的类型覆盖前面的同名属性
interface Foo {
name: string
age: string
address?: string
}
interface Bar {
age: number
sex: string
}
type Merge<T, S> = {
[P in keyof T | keyof S]: P extends keyof S
? S[P]
: P extends keyof T
? T[P]
: never
}
type Result = Merge<Foo, Bar>
// type Result = {
// name: string;
// age: number;
// address?: string;
// sex: string;
// }
infer 类型推断
infer 关键字可以在条件类型中声明泛型变量进行类型推断。
数组操作
// 获取数组第一个元素类型
type First<T extends any[]> = T extends [] ? never : T[0]
type Array1 = ['a', 'b', 'c']
type First1 = First<Array1> // 'a'
type Array2 = []
type First2 = First<Array2> // never
// 获取数组最后一个元素类型
type Last<T extends any[]> = T extends [...infer Rest, infer Last]
? Last
: never
type Last1 = Last<Array1> // 'c'
// 获取数组除第一个元素外的类型
type Rest<T extends any[]> = T extends [infer First, ...infer Rest]
? Rest
: never
type Rest1 = Rest<Array1> // ['b', 'c']
函数类型推断
// 获取函数返回值类型
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type Func1 = (a: string) => number
type Func2 = () => string
interface Func3 {
name: string
}
type Return1 = GetReturnType<Func1> // number
type Return2 = GetReturnType<Func2> // string
type Return3 = GetReturnType<Func3> // never
// 获取函数参数类型
type GetParameters<T> = T extends (...args: infer P) => any ? P : never
type Params1 = GetParameters<Func1> // [a: string]
type Params2 = GetParameters<Func2> // []
Promise 类型推断
// 获取 Promise 的值类型
type UnpackPromise<T> = T extends Promise<infer P> ? P : never
type Promise1 = Promise<string>
type Promise2 = Promise<number[]>
type Unpack1 = UnpackPromise<Promise1> // string
type Unpack2 = UnpackPromise<Promise2> // number[]
枚举与常量枚举
枚举基础
枚举是一组命名常量的集合,让代码更具可读性。
enum Status {
Success = 200,
NotFound = 404,
Error = 500,
Other = 'other'
}
// 使用枚举
function checkStatus(status: Status) {
if (status === Status.Success) {
console.log('操作成功')
}
}
console.log(Status.Success) // 200
枚举的特性
| 特性 | 说明 |
|---|---|
| 既是类型也是值 | 可以用于类型标注,也可以参与逻辑运算 |
| 生成运行时代码 | 编译后会生成 IIFE(立即执行函数) |
| 反向映射 | 数字枚举支持从值到键的反向映射 |
// 数字枚举的反向映射
enum Direction {
Up = 1,
Down,
Left,
Right
}
console.log(Direction.Up) // 1
console.log(Direction[1]) // "Up"
枚举编译结果
枚举在编译后会生成代码:
// 编译后的代码
let Status;
(function (Status) {
Status[Status.Success = 200] = 'Success'
Status[Status.NotFound = 404] = 'NotFound'
Status[Status.Error = 500] = 'Error'
Status[Status.Other = 'other'] = 'Other'
})(Status || (Status = {}))
推荐替代方案:常量对象
在现代 ESM 生态中,推荐使用 const 配合 as const 来替代枚举:
// 常量对象
const Status = {
Success: 200,
NotFound: 404,
Error: 500,
Other: 'other'
} as const
// 获取联合类型
type Status = typeof Status[keyof typeof Status]
// 200 | 404 | 500 | 'other'
// 使用
type StatusType = typeof Status[number] // 自动推断为联合类型
// 使用示例
function handleStatus(status: StatusType) {
if (status === Status.Success) {
console.log('操作成功')
}
}
// 优势:可以被 tree-shaking 优化,保持类型安全
模板字符串类型
模板字符串类型允许你基于字符串字面量类型进行类型组合。
基础用法
type Direction = 'left' | 'right' | 'top' | 'bottom'
type BoxName = 'padding' | 'margin'
// 组合类型
type BoxModel = `${BoxName}-${Direction}`
// "padding-left" | "padding-right" | "padding-top" | "padding-bottom"
// | "margin-left" | "margin-right" | "margin-top" | "margin-bottom"
// 实际应用
type CSSProperties = {
[key in BoxModel]: string
}
const styles: CSSProperties = {
'padding-left': '10px',
'margin-top': '20px'
// ...
}
高级应用 - 动态属性名
interface User {
name: string
age: number
}
// 添加 getter 和 setter 方法
type AddGetter<T> = {
[K in keyof T as `get${Capitalize<K & string>}`]: () => T[K]
}
type AddSetter<T> = {
[K in keyof T as `set${Capitalize<K & string>}`]: (value: T[K]) => void
}
type UserWithAccessors = AddGetter<User> & AddSetter<User>
/*
type UserWithAccessors = {
getName: () => string;
getAge: () => number;
setName: (value: string) => void;
setAge: (value: number) => void;
}
*/
// 使用示例
const user: UserWithAccessors = {
name: '小明',
age: 18,
getName() {
return this.name
},
getAge() {
return this.age
},
setName(value) {
this.name = value
},
setAge(value) {
this.age = value
}
}
模板字面量类型修饰符
// Uppercase - 转换为大写
type Event = `on${Capitalize<'click' | 'hover' | 'focus'>}`
// "onClick" | "onHover" | "onFocus"
// Lowercase - 转换为小写
type Method = `get${Uppercase<'user' | 'post' | 'config'>}`
// "getUser" | "getPost" | "getConfig"
// 模板字面量中的模式匹配
type ExtractFirst<T> = T extends `${infer First}${infer Rest}` ? First : never
type ExtractRest<T> = T extends `${infer First}${infer Rest}` ? Rest : never
type Test1 = ExtractFirst<'hello'> // 'h'
type Test2 = ExtractRest<'hello'> // 'ello'
类型递归
类型递归是 TypeScript 类型系统中最强大的特性之一,可以处理复杂的类型变换。
字符串转联合类型
// 将字符串的每个字符转为联合类型
type StringToUnion<T extends string> = T extends `${infer First}${infer Rest}`
? First | StringToUnion<Rest>
: never
type Letters = StringToUnion<'abc'>
// 'a' | 'b' | 'c'
// 实际应用 - 路径解析
type PathToUnion<T extends string> = T extends `${infer First}/${infer Rest}`
? First | PathToUnion<Rest>
: T
type Paths = PathToUnion<'home/user/profile'>
// 'home' | 'user' | 'profile'
深度可选类型
// 将对象的所有嵌套属性变为可选
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]
}
interface User {
name: string
address: {
street: string
city: string
country: string
}
hobbies: string[]
}
type PartialUser = DeepPartial<User>
/*
type PartialUser = {
name?: string | undefined;
address?: {
street?: string | undefined;
city?: string | undefined;
country?: string | undefined;
} | undefined;
hobbies?: string[] | undefined;
}
*/
深度只读类型
// 将对象的所有嵌套属性变为只读
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K]
}
type ReadonlyUser = DeepReadonly<User>
/*
type ReadonlyUser = {
readonly name: string;
readonly address: {
readonly street: string;
readonly city: string;
readonly country: string;
};
readonly hobbies: readonly string[];
}
*/
树形结构遍历
// 递归提取树节点的 ID
type ExtractIds<T> = T extends { id: infer Id }
? Id | ExtractIds<T extends Array<infer U> ? U : never>
: never
interface TreeNode {
id: string
children?: TreeNode[]
}
type NodeIds = ExtractIds<TreeNode>
// string
type MultipleNodeIds = ExtractIds<TreeNode[]>
// string
常用工具类型实现
基础工具类型
Copy 复制类型
// 复制一个对象类型
type Copy<T extends object> = {
[K in keyof T]: T[K]
}
interface User {
name: string
age: number
}
type CopiedUser = Copy<User> // 与 User 相同
Partial 可选属性
// 将所有属性变为可选
type MyPartial<T> = {
[K in keyof T]?: T[K]
}
// 内部实现原理
interface User {
name: string
age: number
email?: string
}
type OptionalUser = MyPartial<User>
/*
{
name?: string;
age?: number;
email?: string;
}
*/
Readonly 只读属性
// 将所有属性变为只读
type MyReadonly<T> = {
readonly [K in keyof T]: T[K]
}
// 内部实现原理
type ReadonlyUser = MyPartial<User>
/*
{
readonly name: string;
readonly age: number;
readonly email?: string;
}
*/
选择性工具类型
Pick 挑选属性
// 从类型中挑选指定的属性
type MyPick<T, K extends keyof T> = {
[P in K]: T[P]
}
// 内部实现原理
interface User {
id: number
name: string
age: number
email?: string
}
type UserInfo = MyPick<User, 'name' | 'age'>
/*
{
name: string;
age: number;
}
*/
Omit 排除属性
// 从类型中排除指定的属性
type MyOmit<T, K extends keyof any> = {
[P in Exclude<keyof T, K>]: T[P]
}
// 等价于 Pick<T, Exclude<keyof T, K>>
type MyOmitAlternative<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>
// 内部实现原理
interface User {
id: number
name: string
age: number
email?: string
}
type UserProfile = MyOmit<User, 'age' | 'email'>
/*
{
id: number;
name: string;
}
*/
Record 映射类型
// 基于键类型创建对象类型
type MyRecord<K extends string | number | symbol, V> = {
[P in K]: V
}
// 内部实现原理
type Roles = 'admin' | 'user' | 'guest'
type RolePermissions = Record<Roles, string[]>
/*
{
admin: string[];
user: string[];
guest: string[];
}
*/
过滤工具类型
Extract 提取类型
// 从联合类型中提取符合条件的类型
type MyExtract<T, U> = T extends U ? T : never
// 内部实现原理
type Status = 'success' | 'error' | 'pending' | 'loading'
type StatusResults = Extract<Status, 'success' | 'pending'>
// 'success' | 'pending'
Exclude 排除类型
// 从联合类型中排除符合条件的类型
type MyExclude<T, U> = T extends U ? never : T
// 内部实现原理
type Status = 'success' | 'error' | 'pending' | 'loading'
type Errors = Exclude<Status, 'success' | 'pending'>
// 'error' | 'loading'
高级工具类型
Required 必填属性
// 将所有属性变为必填
type MyRequired<T> = {
[P in keyof T]-?: T[P]
}
interface PartialUser {
name?: string
age?: number
}
type RequiredUser = MyRequired<PartialUser>
/*
{
name: string;
age: number;
}
*/
ReturnType 获取返回值类型
// 获取函数返回值的类型
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type Func1 = () => string
type Func2 = (x: number) => { success: boolean, data: any }
type Return1 = MyReturnType<Func1> // string
type Return2 = MyReturnType<Func2> // { success: boolean; data: any }
Parameters 获取参数类型
// 获取函数参数的类型
type MyParameters<T> = T extends (...args: infer P) => any ? P : never
type Func = (a: number, b: string) => boolean
type Params = MyParameters<Func> // [a: number, b: string]
工具类型速查表
| 工具类型 | 作用 | 使用场景 |
|---|---|---|
Partial<T> | 所有属性变为可选 | 表单编辑、更新部分字段 |
Required<T> | 所有属性变为必填 | 数据验证、确保完整性 |
Readonly<T> | 所有属性变为只读 | 配置对象、常量定义 |
Pick<T, K> | 只保留指定属性 | 筛选需要的字段 |
Omit<T, K> | 排除指定属性 | 移除敏感字段、API响应处理 |
Record<K, V> | 基于键类型创建对象 | 字典、映射表 |
Extract<T, U> | 提取联合类型中的部分 | 类型过滤、条件判断 |
Exclude<T, U> | 排除联合类型中的部分 | 类型过滤、错误类型排除 |
ReturnType<T> | 获取函数返回值类型 | 异步处理、Promise链 |
Parameters<T> | 获取函数参数类型 | 参数验证、函数重载 |
实战应用
1. API 响应类型处理
// API 响应基础类型
interface ApiResponse<T> {
code: number
message: string
data: T
}
// 分页响应
interface PaginatedResponse<T> {
items: T[]
total: number
page: number
pageSize: number
}
// 自动提取 API 数据类型
type ApiData<T> = ApiResponse<T>['data']
// 使用示例
interface User {
id: number
name: string
email: string
}
type UserResponse = ApiResponse<User>
type UserListResponse = PaginatedResponse<User>
// 获取用户数据的类型
type UserData = ApiData<UserResponse> // User
type UserListData = ApiData<UserListResponse> // PaginatedResponse<User>
// 自动处理 API 响应的工具
type ApiResponseHandler<T> = T extends ApiResponse<infer U>
? U
: never
2. 组件 Props 类型推导
// 通用组件 Props 类型
interface BaseProps {
className?: string
style?: React.CSSProperties
children?: React.ReactNode
}
// 状态组件 Props
interface StatefulProps<T> extends BaseProps {
initialState: T
onStateChange?: (state: T) => void
}
// 自动推导组件 Props
type ComponentProps<T> = T extends React.FC<infer P> ? P : never
// 使用示例
interface ButtonProps extends BaseProps {
onClick: () => void
disabled?: boolean
}
type ExtractedProps = ComponentProps<React.FC<ButtonProps>>
// ButtonProps
3. 路由参数类型推导
// 路由参数类型
type RouteParams<T extends string> = T extends `${string}:${infer Param}${infer Rest}`
? Param | RouteParams<Rest>
: never
// 示例路由
type UserRoute = '/user/:id/profile'
type ProductRoute = '/product/:category/:id'
type UserParams = RouteParams<UserRoute> // 'id'
type ProductParams = RouteParams<ProductRoute> // 'category' | 'id'
// 动态路由生成
type CreatePath<T extends string, P extends Record<string, string>>
= T extends `${string}:${infer Param}${infer Rest}`
? `${P[Param]}${CreatePath<Rest, P>}`
: T
type UserPath = CreatePath<UserRoute, { id: '123' }>
// '/user/123/profile'
4. 状态管理类型增强
// Redux Action 类型
interface Action<T = any> {
type: string
payload?: T
}
// Reducer 类型
type Reducer<S, A extends Action = Action> = (state: S, action: A) => S
// 自动推导 Action 类型
type ActionType<T> = T extends Action<infer P> ? P : never
// 使用示例
interface UserAction extends Action<{ userId: number }> {
type: 'SET_USER'
}
type UserActionPayload = ActionType<UserAction> // { userId: number }
5. 表单验证类型
// 表单规则类型
interface ValidationRule<T> {
required?: boolean
min?: number
max?: number
pattern?: RegExp
custom?: (value: T) => boolean | string
}
// 表单字段类型
interface FormField<T> {
value: T
error?: string
touched: boolean
}
// 表单类型
type Form<T> = {
[K in keyof T]: FormField<T[K]>
}
// 表单验证工具
type ValidateForm<T> = (form: Form<T>) => { valid: boolean, errors: Partial<Record<keyof T, string>> }
// 使用示例
interface LoginForm {
username: string
password: string
rememberMe: boolean
}
type LoginFormState = Form<LoginForm>
const validateLogin: ValidateForm<LoginForm> = (form) => {
const errors: Partial<Record<keyof LoginForm, string>> = {}
if (form.username.value.length < 3) {
errors.username = '用户名至少3个字符'
}
if (form.password.value.length < 6) {
errors.password = '密码至少6个字符'
}
return {
valid: Object.keys(errors).length === 0,
errors
}
}
总结
TypeScript 的高级类型系统虽然复杂,但一旦掌握,就能编写出更加类型安全、更加优雅的代码。本文介绍的核心概念包括:
- 类型查询与操作:使用
typeof和索引访问类型进行类型推导 - 键值操作:通过
keyof和in进行类型映射 - 条件类型:实现复杂的类型逻辑判断
- 类型推断:使用
infer进行自动类型推导 - 枚举与常量:了解枚举的特性和替代方案
- 模板字符串类型:实现动态类型组合
- 类型递归:处理复杂的类型变换
- 工具类型:掌握常用工具类型的实现原理
通过实战应用示例,我们可以看到这些高级类型在实际开发中的强大作用。建议在学习过程中多实践,多思考,才能真正掌握 TypeScript 类型系统的精髓。