TypeScript 中的装包和拆包
概念解释
在 TypeScript 中,装包(Wrapping)和拆包(Unwrapping)通常指:
- 装包:将普通对象转换为代理对象,为其属性添加getter/setter等访问器
- 拆包:从代理对象中获取原始值
基础示例
- 代理包装器类型定义
typescript
// 单个值的代理包装器
type Proxy<T> = {
get(): T;
set(value: T): void;
};
// 将对象的所有属性转换为代理的类型
type Proxify<T> = {
[P in keyof T]: Proxy<T[P]>;
};
- 装包函数实现
typescript
function proxify<T>(obj: T): Proxify<T> {
let result = {} as Proxify<T>;
for (let key in obj) {
let value = obj[key];
result[key] = {
get() {
return value;
},
set: (newValue) => (value = newValue),
};
}
return result;
}
- 使用示例
typescript
// 原始对象
let props = {
name: "张三",
age: 25
};
// 装包
let proxyProps = proxify(props);
// 访问代理属性(拆包)
console.log(proxyProps.name.get()); // "张三"
proxyProps.age.set(26);
console.log(proxyProps.age.get()); // 26
拆包函数示例
typescript
// 拆包函数
function unproxify<T>(proxifiedObj: Proxify<T>): T {
let result = {} as T;
for (let key in proxifiedObj) {
result[key] = proxifiedObj[key].get();
}
return result;
}
// 使用拆包
let originalProps = unproxify(proxyProps);
console.log(originalProps.name); // "张三"
实际应用场景
- 属性访问控制
typescript
interface User {
name: string;
age: number;
}
const user: User = {
name: "李四",
age: 30
};
// 装包添加访问控制
const proxiedUser = proxify(user);
// 可以在 get/set 中添加验证逻辑
proxiedUser.age.set(25); // 可以添加年龄验证
console.log(proxiedUser.name.get()); // 可以添加访问日志
- 响应式数据实现
typescript
function createReactive<T extends object>(obj: T): Proxify<T> {
return proxify(obj);
}
const state = createReactive({
count: 0,
message: "hello"
});
// 可以在 set 时触发更新
state.count.set(state.count.get() + 1);
注意事项
- 性能考虑
typescript
// 装包会创建额外的对象,增加内存开销
const largeObject = { /* 大量属性 */ };
const proxified = proxify(largeObject); // 谨慎使用
- 类型安全
typescript
// 确保类型定义完整
type SafeProxy<T> = {
get(): T;
set(value: T): void;
readonly value: T; // 可以添加额外的类型安全保证
};
- 嵌套对象处理
typescript
// 处理嵌套对象需要递归装包
function deepProxify<T>(obj: T): Proxify<T> {
let result = {} as Proxify<T>;
for (let key in obj) {
const value = obj[key];
if (typeof value === 'object' && value !== null) {
result[key] = deepProxify(value);
} else {
result[key] = {
get() { return value; },
set: (newValue) => value = newValue,
};
}
}
return result;
}
最佳实践
- 明确装包的目的
- 只在需要控制属性访问时使用装包
- 避免过度使用,可能影响代码可读性
- 保持类型安全
- 使用泛型确保类型推导
- 为代理对象定义清晰的接口
- 考虑使用内置的 Proxy
typescript
// 考虑使用 JavaScript 的 Proxy 对象
const handler = {
get: (target, prop) => target[prop],
set: (target, prop, value) => {
target[prop] = value;
return true;
}
};
const proxy = new Proxy(target, handler);