TypeScript 类型系统深度进阶
TypeScript 的强大之处在于其灵活而强大的类型系统。本文从进阶角度深度讲解,帮你写出更安全、更优雅的代码。
1. 泛型 (Generics) - 类型参数化
基础泛型函数
// ❌ 不好: 失去类型信息
function getFirstElement(arr: any[]): any {
return arr[0]
}
// ✅ 好: 使用泛型,保留类型信息
function getFirstElement<T>(arr: T[]): T {
return arr[0]
}
// 使用时自动推导类型
const numbers = [1, 2, 3]
const first = getFirstElement(numbers) // first: number
泛型约束 (Generic Constraints)
// 定义约束条件
interface HasLength {
length: number
}
// 泛型 T 必须有 length 属性
function getLength<T extends HasLength>(item: T): number {
return item.length
}
getLength("hello") // ✅ 字符串有 length
getLength([1, 2, 3]) // ✅ 数组有 length
getLength(123) // ❌ 数字没有 length,编译错误
泛型工具类型
// 1. Partial - 使所有属性可选
interface User {
name: string
age: number
email: string
}
type PartialUser = Partial<User>
// 等同于:
// {
// name?: string
// age?: number
// email?: string
// }
// 2. Required - 使所有属性必需
type RequiredUser = Required<PartialUser>
// 回到完全必需的 User
// 3. Readonly - 使所有属性只读
type ReadonlyUser = Readonly<User>
// 4. Pick - 选择特定属性
type UserPreview = Pick<User, 'name' | 'email'>
// {
// name: string
// email: string
// }
// 5. Omit - 排除特定属性
type UserWithoutEmail = Omit<User, 'email'>
// {
// name: string
// age: number
// }
// 6. Record - 创建对象类型
type PageStatus = 'home' | 'about' | 'contact'
type PageConfig = Record<PageStatus, { title: string; url: string }>
// {
// home: { title: string; url: string }
// about: { title: string; url: string }
// contact: { title: string; url: string }
// }
2. 条件类型 (Conditional Types) - 类型级 if-else
// 基础条件类型
type IsString<T> = T extends string ? true : false
type A = IsString<'hello'> // true
type B = IsString<123> // false
// 实用例子: 获取数组元素类型
type Flatten<T> = T extends Array<infer U> ? U : T
type Str = Flatten<string[]> // string
type Num = Flatten<number> // number
type Arr = Flatten<Array<boolean>> // boolean
// infer 关键字: 推导类型变量
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never
type Func = (x: number) => string
type FuncReturn = GetReturnType<Func> // string
// 条件类型分发 (Distributive)
type ToArray<T> = T extends any ? T[] : never
type StrOrNum = string | number
type StrOrNumArray = ToArray<StrOrNum>
// (string | number)[] 或
// string[] | number[] ?
// 答案是后者!条件类型在联合类型上分发
// 防止分发: 用 [] 包裹
type ToArray2<T> = [T] extends [any] ? T[] : never
3. 映射类型 (Mapped Types) - 批量生成类型
// 基础映射类型
type Readonly2<T> = {
readonly [K in keyof T]: T[K]
}
type User = {
name: string
age: number
}
type ReadonlyUser2 = Readonly2<User>
// {
// readonly name: string
// readonly age: number
// }
// 映射类型的实用例子
// 1. Getters: 为每个属性生成 getter 函数
type Getters<T> = {
[K in keyof T as `get${Capitalize<K & string>}`]: () => T[K]
}
type GettersUser = Getters<User>
// {
// getName: () => string
// getAge: () => number
// }
// 2. Setters: 为每个属性生成 setter 函数
type Setters<T> = {
[K in keyof T as `set${Capitalize<K & string>}`]: (value: T[K]) => void
}
// 3. Nullable: 所有属性可为 null
type Nullable<T> = {
[K in keyof T]: T[K] | null
}
type NullableUser = Nullable<User>
// {
// name: string | null
// age: number | null
// }
// 4. ApiResponse: 包装 API 响应
type ApiResponse<T> = {
[K in keyof T]: {
data: T[K]
loading: boolean
error: Error | null
}
}
type UserResponse = ApiResponse<User>
// {
// name: { data: string; loading: boolean; error: Error | null }
// age: { data: number; loading: boolean; error: Error | null }
// }
4. 装饰器 (Decorators) - 修改类和方法
类装饰器
// 基础类装饰器
function Observer(target: Function) {
console.log(`Creating observer for ${target.name}`)
}
@Observer
class User {
name: string = 'John'
greet() {
console.log(`Hello, ${this.name}`)
}
}
// 装饰器工厂
function LogClass<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
constructor(...args: any[]) {
super(...args)
console.log(`${constructor.name} 实例已创建`)
}
}
}
@LogClass
class UserWithLog {
name = 'Alice'
}
new UserWithLog() // 输出: UserWithLog 实例已创建
方法装饰器
// 方法装饰器: 添加日志
function LogMethod(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const originalMethod = descriptor.value
descriptor.value = function(...args: any[]) {
console.log(`调用 ${propertyKey},参数:`, args)
const result = originalMethod.apply(this, args)
console.log(`${propertyKey} 返回:`, result)
return result
}
return descriptor
}
class Calculator {
@LogMethod
add(a: number, b: number): number {
return a + b
}
}
const calc = new Calculator()
calc.add(2, 3)
// 输出:
// 调用 add,参数: [2, 3]
// add 返回: 5
属性装饰器
// 属性装饰器: 验证
function Validate(target: any, propertyKey: string) {
let value: any
const getter = () => value
const setter = (newValue: any) => {
if (typeof newValue !== 'string') {
throw new Error(`${propertyKey} must be a string`)
}
value = newValue
}
Object.defineProperty(target, propertyKey, {
get: getter,
set: setter,
enumerable: true,
configurable: true
})
}
class Person {
@Validate
name: string = ''
}
const person = new Person()
person.name = 'John' // ✅ 成功
person.name = 123 // ❌ 抛出错误
5. 实战模式
模式 1: 深层类型安全的 API 客户端
// 定义 API 端点类型
interface ApiEndpoints {
'users': {
method: 'GET'
params: { id: number }
response: { name: string; age: number }
}
'posts': {
method: 'POST'
params: { title: string; content: string }
response: { id: number }
}
}
// 创建类型安全的 API 调用函数
type ApiCall<K extends keyof ApiEndpoints> = (
key: K,
params: ApiEndpoints[K]['params']
) => Promise<ApiEndpoints[K]['response']>
const api: ApiCall<any> = async (endpoint, params) => {
const response = await fetch(`/api/${endpoint}`, {
method: 'POST',
body: JSON.stringify(params)
})
return response.json()
}
// 使用: 完全类型安全
async function getUser() {
const user = await api('users', { id: 1 })
console.log(user.name) // ✅ 知道有 name 属性
// console.log(user.title) // ❌ 错误: 没有 title
}
模式 2: Store 类型推导
// 定义 Store
const store = {
state: {
user: { name: 'John', age: 30 },
posts: [{ id: 1, title: 'Hello' }],
ui: { theme: 'dark' }
},
getters: {
userName() {
return this.state.user.name
},
postCount() {
return this.state.posts.length
}
}
}
// 自动推导所有键和类型
type StoreState = typeof store.state
type StateKeys = keyof StoreState // 'user' | 'posts' | 'ui'
type UserState = StoreState['user'] // { name: string; age: number }
// 创建访问函数,类型完全推导
function useState<K extends keyof StoreState>(key: K) {
return store.state[key] // 返回类型自动推导为具体类型
}
const user = useState('user') // { name: string; age: number }
const posts = useState('posts') // { id: number; title: string }[]
模式 3: 响应式代理
// 创建响应式对象的类型安全代理
function reactive<T extends object>(target: T): T {
return new Proxy(target, {
get(obj, prop) {
console.log(`访问属性: ${String(prop)}`)
return obj[prop as keyof T]
},
set(obj, prop, value) {
console.log(`设置 ${String(prop)} = ${value}`)
obj[prop as keyof T] = value
return true
}
})
}
const user = reactive({ name: 'John', age: 30 })
user.name = 'Alice' // ✅ 有完整的类型检查
// user.email = 'test@com' // ❌ 错误: 没有 email 属性
6. 类型检查最佳实践
优先使用联合类型而不是 any
// ❌ 避免
function process(value: any) {
return value.toUpperCase()
}
// ✅ 改为
function process(value: string | string[]) {
if (Array.isArray(value)) {
return value.map(v => v.toUpperCase())
}
return value.toUpperCase()
}
使用类型守卫
// 类型谓词
function isString(value: any): value is string {
return typeof value === 'string'
}
// 使用
function process(value: string | number) {
if (isString(value)) {
console.log(value.toUpperCase()) // 此时 TS 知道是 string
} else {
console.log(value.toFixed(2)) // 此时 TS 知道是 number
}
}
严格的 null 检查
// 启用 tsconfig.json: "strictNullChecks": true
interface User {
name: string
email?: string // 可选
}
function sendEmail(user: User) {
// ❌ 错误: email 可能是 undefined
// console.log(user.email.length)
// ✅ 正确: 先检查
if (user.email) {
console.log(user.email.length)
}
// 或使用可选链
console.log(user.email?.length)
}
TypeScript 配置最佳实践
{
"compilerOptions": {
// 严格模式
"strict": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
// 模块和导出
"module": "ESNext",
"target": "ES2020",
"moduleResolution": "node",
// 类型检查
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
}
}
常见错误和解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| "Type '...' is not assignable to type '...'" | 类型不兼容 | 使用 as const 或明确类型 |
| "Property '...' does not exist" | 属性不存在 | 检查对象类型定义 |
| "Cannot invoke an expression" | 调用非函数 | 检查函数类型定义 |
| "Object is possibly 'undefined'" | 空值检查 | 启用 strictNullChecks |
| "Argument is not assignable" | 参数类型错误 | 使用类型守卫或断言 |
总结:从编程到 TypeScript
// 第一阶段: 基础类型
let name: string = "John"
let age: number = 30
// 第二阶段: 接口和类型别名
interface User {
name: string
age: number
}
// 第三阶段: 泛型
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}
// 第四阶段: 条件类型
type IsArray<T> = T extends Array<any> ? true : false
// 第五阶段: 映射类型
type Getters<T> = {
[K in keyof T as `get${Capitalize<K & string>}`]: () => T[K]
}
// 掌握这些,你就是 TypeScript 高手!


