TypeScript Proxy Reflect 实战:3种数据验证模式与1个观察者模式实现

📅 2026/7/11 22:25:48
TypeScript Proxy  Reflect 实战:3种数据验证模式与1个观察者模式实现
TypeScript Proxy Reflect 实战3种数据验证模式与1个观察者模式实现在TypeScript的世界里Proxy和Reflect这两个API常常被开发者忽视但它们却是元编程领域的利器。本文将带你深入探索如何利用这两个工具构建强大的数据验证系统和响应式观察者模式。1. Proxy与Reflect基础概念Proxy对象用于创建一个对象的代理从而拦截和自定义基本操作如属性查找、赋值、枚举、函数调用等。Reflect则是一个内置对象它提供拦截JavaScript操作的方法这些方法与Proxy handlers的方法一一对应。让我们看一个最简单的Proxy示例const target { name: John }; const handler { get(target, prop) { console.log(访问属性: ${prop}); return target[prop]; } }; const proxy new Proxy(target, handler); console.log(proxy.name); // 输出: 访问属性: name \n JohnReflect通常与Proxy配合使用它提供了操作对象的标准化方法const obj { x: 1, y: 2 }; // 传统方式 obj.x 3; // Reflect方式 Reflect.set(obj, x, 3);2. 三种数据验证模式实现2.1 类型检查验证器我们可以利用Proxy的set trap来实现类型检查interface User { name: string; age: number; email?: string; } function createTypeValidatorT(initialValue: T): T { return new Proxy(initialValue, { set(target, prop, value) { const expectedType typeof Reflect.get(target, prop); if (expectedType ! undefined typeof value ! expectedType) { throw new TypeError(属性${String(prop)}期望类型为${expectedType}, 但收到${typeof value}); } return Reflect.set(target, prop, value); } }) as T; } const user createTypeValidatorUser({ name: , age: 0 }); user.name Alice; // 正常 user.age 30; // 抛出TypeError2.2 范围限制验证器对于数值属性我们经常需要限制其范围function createRangeValidatorT(obj: T, rules: Recordstring, { min?: number, max?: number }): T { return new Proxy(obj, { set(target, prop, value) { const rule rules[prop as string]; if (rule) { if (typeof value ! number) { throw new TypeError(属性${String(prop)}必须是数字); } if (rule.min ! undefined value rule.min) { throw new RangeError(属性${String(prop)}不能小于${rule.min}); } if (rule.max ! undefined value rule.max) { throw new RangeError(属性${String(prop)}不能大于${rule.max}); } } return Reflect.set(target, prop, value); } }); } const product createRangeValidator( { price: 0, stock: 0 }, { price: { min: 0 }, stock: { min: 0, max: 1000 } } ); product.price 99; // 正常 product.stock 1001; // 抛出RangeError2.3 属性保护验证器某些属性可能需要特殊保护比如只读属性或需要权限验证的属性function createProtectedObjectT(obj: T, readOnlyProps: (keyof T)[] []): T { return new Proxy(obj, { set(target, prop, value, receiver) { if (readOnlyProps.includes(prop as keyof T)) { throw new Error(属性${String(prop)}是只读的); } return Reflect.set(target, prop, value, receiver); }, deleteProperty(target, prop) { if (readOnlyProps.includes(prop as keyof T)) { throw new Error(属性${String(prop)}是受保护的不能删除); } return Reflect.deleteProperty(target, prop); } }); } const config createProtectedObject( { apiKey: 12345, theme: light }, [apiKey] ); config.theme dark; // 正常 config.apiKey new; // 抛出错误3. 观察者模式实现利用Proxy的拦截能力我们可以轻松实现一个响应式系统type ObserverT (key: keyof T, newValue: any, oldValue: any) void; function createObservableT extends object(target: T): [T, (observer: ObserverT) void] { const observers new SetObserverT(); const proxy new Proxy(target, { set(target, prop, value, receiver) { const oldValue Reflect.get(target, prop, receiver); const result Reflect.set(target, prop, value, receiver); if (result oldValue ! value) { observers.forEach(observer observer(prop as keyof T, value, oldValue)); } return result; } }); const subscribe (observer: ObserverT) { observers.add(observer); return () observers.delete(observer); }; return [proxy, subscribe]; } // 使用示例 const [state, subscribe] createObservable({ count: 0, name: }); const unsubscribe subscribe((key, newValue, oldValue) { console.log(状态变化: ${String(key)} 从 ${oldValue} 变为 ${newValue}); }); state.count 1; // 输出: 状态变化: count 从 0 变为 1 state.name Alice; // 输出: 状态变化: name 从 变为 Alice4. 高级技巧与性能考量4.1 选择性代理对于大型对象我们可以选择性地只代理需要监控的属性function createSelectiveProxyT extends object( target: T, propsToProxy: (keyof T)[] ): T { return new Proxy(target, { get(target, prop, receiver) { if (propsToProxy.includes(prop as keyof T)) { console.log(读取属性: ${String(prop)}); } return Reflect.get(target, prop, receiver); }, set(target, prop, value, receiver) { if (propsToProxy.includes(prop as keyof T)) { console.log(设置属性: ${String(prop)} ${value}); } return Reflect.set(target, prop, value, receiver); } }); } const data createSelectiveProxy( { id: 1, name: , secret: confidential }, [name] ); data.name Bob; // 会打印日志 data.secret new; // 不会打印日志4.2 性能优化建议虽然Proxy功能强大但过度使用可能影响性能避免深层代理只代理必要的对象层级减少handler中的复杂逻辑拦截器中的逻辑应尽可能简单考虑缓存策略对于频繁访问的属性可以在handler中添加缓存使用Revocable Proxy对于临时需要的代理可以使用Proxy.revocable()const { proxy, revoke } Proxy.revocable({}, { get(target, prop) { return 访问了${String(prop)}; } }); console.log(proxy.test); // 输出: 访问了test revoke(); console.log(proxy.test); // 抛出TypeError在实际项目中我发现Proxy和Reflect的组合特别适合以下场景表单验证实时验证用户输入状态管理构建轻量级响应式系统API封装拦截和转换数据访问日志记录跟踪对象属性的访问和修改Proxy和Reflect为TypeScript开发者提供了强大的元编程能力合理使用它们可以大幅提升代码的灵活性和可维护性。