Appearance
ArkTS 泛型与 Record
泛型是代码复用的核心机制,Record 是构建映射类型的利器。
1. 泛型的本质
泛型允许在定义函数、类、接口时不指定具体类型,而是在使用时再指定。核心目的:代码复用 + 类型安全。
typescript
// 没有泛型:每种类型都要写一个函数
function identityNumber(n: number): number { return n }
function identityString(s: string): string { return s }
function identityBoolean(b: boolean): boolean { return b }
// 使用泛型:一个函数搞定所有类型
function identity<T>(arg: T): T {
return arg
}2. 泛型的约束(Type Constraints)
2.1 extends 约束
typescript
// 限制 T 必须具有 length 属性
function getLength<T extends { length: number }>(arg: T): number {
return arg.length // ✅ 编译器知道 arg 有 length
}
getLength('hello') // ✅ number
getLength([1, 2, 3]) // ✅ number
// getLength(42) // ❌ 编译错误:number 没有 length 属性2.2 接口约束
typescript
interface HasId {
id: number
}
// T 必须实现 HasId 接口
function processWithId<T extends HasId>(item: T): string {
return `处理 ID: ${item.id}` // ✅ 编译器知道 item.id 存在
}
class User implements HasId { id: number; name: string }
class Product implements HasId { id: number; price: number }
processWithId(new User()) // ✅
processWithId(new Product()) // ✅2.3 多个约束
typescript
// T 必须同时满足多个约束
function process<T extends HasId & { name: string }>(item: T): void {
console.log(item.id, item.name)
}3. 泛型工具类型
3.1 Partial<T>
typescript
interface User {
id: number
name: string
email: string
}
// 所有属性变为可选
function updateUser(partial: Partial<User>): User {
return {
id: 0,
name: 'Unknown',
email: '',
...partial
}
}
const user = updateUser({ name: '小明' }) // ✅ 只需传部分字段3.2 Required<T>
typescript
// 所有属性变为必填
interface OptionalUser {
id?: number
name?: string
}
const full: Required<OptionalUser> = {
id: 1,
name: '小明'
}3.3 Pick<T, Keys>
typescript
interface User {
id: number
name: string
email: string
age: number
}
// 只选取指定属性
type UserBrief = Pick<User, 'id' | 'name'>
const brief: UserBrief = { id: 1, name: '小明' }3.4 Omit<T, Keys>
typescript
// 排除指定属性
type UserPublic = Omit<User, 'email'>
const pub: UserPublic = { id: 1, name: '小明', age: 25 }3.5 Readonly<T>
typescript
const config: Readonly<User> = {
id: 1,
name: '小明',
email: 'xiaoming@test.com',
age: 25
}
// config.id = 2 // ❌ 编译错误4. Record 深度解析
4.1 基本语法
typescript
// Record<Keys, Type>:构建键类型到值类型的映射
type UserMap = Record<string, User>
type NumberMap = Record<number, string>
type BooleanMap = Record<'yes' | 'no', boolean>
// 实际使用
const userMap: UserMap = {
'user1': { id: 1, name: '小明', email: 'a@b.com', age: 25 },
'user2': { id: 2, name: '小红', email: 'c@d.com', age: 22 }
}4.2 Record 的优势 vs 普通对象
typescript
// ❌ 普通对象:类型不精确
const obj1: Record<string, any> = {} // 完全失去类型安全
const obj2 = {} // 类型是 {},没有任何类型提示
// ✅ Record:保持类型安全
const obj3: Record<string, User> = {} // key 是 string,value 是 User4.3 Record 的高级用法
typescript
// 用枚举的 key 构建映射
enum Status {
Pending = 'pending',
Active = 'active',
Inactive = 'inactive'
}
type StatusMap = Record<Status, string>
const statusLabels: StatusMap = {
[Status.Pending]: '待处理',
[Status.Active]: '已激活',
[Status.Inactive]: '已停用'
}
// 用 keyof 构建
type UserKeys = keyof User
type UserDict = Record<UserKeys, any>
// 使用
const userDict: UserDict = {
id: 1,
name: '小明',
email: 'test@test.com',
age: 25
}5. 泛型在实战中的应用
5.1 通用 API 响应封装
typescript
interface ApiResponse<T> {
code: number
data: T
message: string
timestamp: number
}
// 泛型函数:自动推断返回类型
async function request<T>(url: string): Promise<ApiResponse<T>> {
// ... 网络请求
return {
code: 200,
data: null as unknown as T,
message: 'OK',
timestamp: Date.now()
}
}
// 使用:自动推断 User 类型
const result = await request<User>('/api/user/1')
const name: string = result.data.name // ✅ 自动推断
// 使用:自动推断数组类型
const list = await request<User[]>('/api/users')
const first: User = list.data[0] // ✅ 自动推断5.2 泛型 Repository 模式
typescript
interface IRepository<T extends { id: number }> {
findById(id: number): T | null
findAll(): T[]
create(item: T): T
update(id: number, item: Partial<T>): T | null
delete(id: number): boolean
}
class UserRepo implements IRepository<User> {
findById(id: number): User | null { return null }
findAll(): User[] { return [] }
create(item: User): User { return item }
update(id: number, item: Partial<User>): User | null { return null }
delete(id: number): boolean { return true }
}6. 面试高频考点
Q1: Record 相比 Object 有什么优势?
回答:Record 提供了更明确的类型约束(键类型和值类型都确定),有利于编译器优化 Hidden Class 的查找效率,比普通的 {} 字面量性能更好,IDE 也能提供准确的自动补全。
Q2: 泛型的约束怎么用?
回答:用 extends 关键字约束泛型参数的范围。例如 T extends HasId 确保 T 必须有 id 属性。可以组合多个约束。
Q3: Partial / Pick / Omit 的区别?
回答:
Partial<T>:所有属性变为可选Pick<T, K>:只选取 K 指定的属性Omit<T, K>:排除 K 指定的属性
🐱 小猫提示:泛型是 ArkTS 面试的进阶考点。记住 Record 的核心优势是"明确的类型约束 + Hidden Class 优化",泛型的核心思想是"代码复用 + 类型安全"。