为了了解Proxy是如何工作的,我想尝试创建一个Proxied类,它可以处理任何属性名称,即使是类中不存在的属性名称,同时仍然可以访问旧属性。当一个类被代理时,我认为Typescript会让你在本机上完成这项工作(因为这毕竟是代理的用例之一(,但我遗憾地发现事实并非如此。当我尝试做这样的事情时:
const handler = {
get: (target: any, key: string) => {
return key;
},
set: (target: any, key: string, value: any) => {
console.log(target, key, value);
return true;
}
};
class ProxTest {
private storage: {[key: string]: any} = {};
public a: number;
constructor() {
return new Proxy(this, handler);
}
}
const c = new ProxTest();
c.val = "b"; //Gives error
我在尝试访问c.val
时出错。我发现了一个";"溶液";,那就是使用";索引签名";并将//@ts ignore添加到所有其他属性中。
const handler = {
get: (target: any, key: string) => {
return key;
},
set: (target: any, key: string, value: any) => {
console.log(target, key, value);
return true;
}
};
class ProxTest {
//@ts-ignore
private storage: {[key: string]: any} = {};
//@ts-ignore
public a: number;
[key: string]: any;
constructor() {
return new Proxy(this, handler);
}
}
const c = new ProxTest();
c.val = "b";
到目前为止,这是我找到的唯一解决方案,老实说,我真的不喜欢。有其他方法吗?
需要明确的是,这段代码只是一般问题的一个例子(不能在代理对象上使用任意命名的属性(,它的形状或形式与我实际使用的某些代码无关。但由于这似乎是一个应该能够做的事情,当使用代理在打字脚本,我想找到一个通用的解决方案
因此,根据评论,我误解了Proxy的工作方式,我认为它的工作方式就像PHP魔术方法(也就是说,只有在尝试访问未知属性时才会触发get/set陷阱(,相反,它适用于所有属性,甚至是对象中已经存在的属性
我假设typescript会自动检测一个被代理的类,并允许您像在它被代理之前一样使用该类的正常值,而它在尝试访问未知属性时不会出错,这可能就是为什么我不能清楚地说明我在问什么的原因
默认情况下,使用Proxy时,typescript会将类型更改为"任何";,这当然可以让你在对象上使用任何类型的属性而不会出错,代价是失去对现有属性的直接访问(这实际上与javascript一致,因为当你代理一个类并声明get/set时,所有东西都会通过这些方法(
因此,我真正想要实现的是拥有一个代理类,在这个类中,我仍然可以访问已经存在的属性,并且typescript可以正确地推断已经存在属性的类型,同时在尝试访问不存在的属性时仍然不会出错。我想出了这个解决方案:
interface ProxTestStorage extends ProxTestConstructor {
[key: string]: any;
}
interface ProxTestConstructor {
new(): ProxTestImpl & ProxTestStorage;
}
class ProxTestImpl {
protected storage: {[key: string]: any} = {};
public a: number = 3;
}
const handler = {
get: (target: any, key: string) => {
console.log(target, key);
return Reflect.get(key in target ? target : target.storage, key);
},
set: (target: any, key: string, value: any) => {
console.log(target, key);
return Reflect.set(key in target ? target : target.storage, key, value);
}
};
const ProxTest = ProxTestImpl as any as ProxTestImpl & ProxTestStorage;
const c = new Proxy<typeof ProxTest>(new ProxTest(), handler);
c.val = "b"; //No error, type is any
c.a = 2; //No error, type is number
console.log(c); // ...{a: 2, storage: {val: "b"}}...
当然,get和set需要正确实现,以允许您访问现有的属性(使用"in"的缺点是需要初始化属性才能看到,但由于这"只是"一个理解Proxies的练习,我同意它(,它们必须与类型的逻辑相一致。通过这样声明类型,我必须使用";ProxTest的类型";当使用ProxTest作为类型时(与普通类不同(,但我想现在这是一个小问题,超出了我的问题范围。
感谢@jcalz的耐心:(