TypeScript:能否合并泛型ObservableObject与T并基于keyof识别字段类型?
问题解答
1. 直接访问ObservableObject的成员(无需调用get)
可以通过类型层面的交叉类型+运行时代理/动态挂载实现,两种方案如下:
方案一:Proxy代理拦截属性读写
用Proxy包装实例,自动映射get/set方法;类型上让实例同时具备ObservableObject<T>和T的特性:
class ObservableObject<T> { private _data: T; constructor(value: T) { this._data = { ...value }; } get<K extends keyof T>(name: K): T[K] { return this._data[name]; } set<K extends keyof T>(name: K, value: T[K]): void { this._data[name] = value; // 此处可添加Observable的状态通知逻辑 } } // 包装工具函数 function createObservable<T>(value: T): ObservableObject<T> & T { const observable = new ObservableObject(value); return new Proxy(observable as ObservableObject<T> & T, { get(target, prop: keyof T) { if (typeof prop === 'string' && prop in target._data) { return target.get(prop); } return (target as any)[prop]; }, set(target, prop: keyof T, value) { if (typeof prop === 'string' && prop in target._data) { target.set(prop, value); return true; } (target as any)[prop] = value; return true; } }); } // 使用示例 interface Dog { name: string; age: number } let observableDog = createObservable<Dog>({ name: "Fifi", age: 1 }); // 直接访问属性 console.log(observableDog.name); // "Fifi" observableDog.age = 2; console.log(observableDog.get("age")); // 2
方案二:构造函数动态挂载属性
在实例初始化时,把源对象的属性直接挂载到实例上,绑定到get/set方法:
class ObservableObject<T> { private _data: T; constructor(value: T) { this._data = { ...value }; // 动态生成属性访问器 Object.keys(value).forEach(key => { Object.defineProperty(this, key, { get: () => this.get(key as keyof T), set: (val) => this.set(key as keyof T, val), enumerable: true }); }); } get<K extends keyof T>(name: K): T[K] { return this._data[name]; } set<K extends keyof T>(name: K, value: T[K]): void { this._data[name] = value; // 状态通知逻辑 } } // 使用示例 interface Dog { name: string; age: number } let observableDog = new ObservableObject<Dog>({ name: "Fifi", age: 1 }); console.log(observableDog.name); // "Fifi" observableDog.name = "Max"; console.log(observableDog.get("name")); // "Max"
2. TypeScript基于keyof识别字段类型
完全可以,只需给set方法添加泛型类型约束,让value的类型严格匹配目标字段的类型:
修改ObservableObject的方法定义:
class ObservableObject<T> { constructor(value: T); get<K extends keyof T>(name: K): T[K]; // 用泛型K约束value为对应字段的类型 set<K extends keyof T>(name: K, value: T[K]): void; }
此时传入错误类型会直接触发TypeScript报错:
interface Dog { name: string; age: number } let observableDog = new ObservableObject<Dog>({ name: "Fifi", age: 1 }); observableDog.set("name", 0); // 类型错误:不能将类型“number”分配给类型“string”
原定义中set方法的value未指定类型,导致TypeScript无法校验匹配关系,添加泛型约束后即可实现精确的类型检查。
内容的提问来源于stack exchange,提问作者watch_it_burn
相关产品推荐
相关产品推荐

