Skip to content

TypeScript 中的装包和拆包

概念解释

在 TypeScript 中,装包(Wrapping)和拆包(Unwrapping)通常指:

  • 装包:将普通对象转换为代理对象,为其属性添加getter/setter等访问器
  • 拆包:从代理对象中获取原始值

基础示例

  1. 代理包装器类型定义
typescript
// 单个值的代理包装器
type Proxy<T> = {
    get(): T;
    set(value: T): void;
};

// 将对象的所有属性转换为代理的类型
type Proxify<T> = {
    [P in keyof T]: Proxy<T[P]>;
};
  1. 装包函数实现
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;
}
  1. 使用示例
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); // "张三"

实际应用场景

  1. 属性访问控制
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()); // 可以添加访问日志
  1. 响应式数据实现
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);

注意事项

  1. 性能考虑
typescript
// 装包会创建额外的对象,增加内存开销
const largeObject = { /* 大量属性 */ };
const proxified = proxify(largeObject); // 谨慎使用
  1. 类型安全
typescript
// 确保类型定义完整
type SafeProxy<T> = {
    get(): T;
    set(value: T): void;
    readonly value: T; // 可以添加额外的类型安全保证
};
  1. 嵌套对象处理
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;
}

最佳实践

  1. 明确装包的目的
  • 只在需要控制属性访问时使用装包
  • 避免过度使用,可能影响代码可读性
  1. 保持类型安全
  • 使用泛型确保类型推导
  • 为代理对象定义清晰的接口
  1. 考虑使用内置的 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);