Typescript 编译器不知道 Class 上的 ES6 代理陷阱
Typescript Compiler Does Not Know About ES6 Proxy Trap on Class
我有一个摘要class:
abstract class Foo {
abstract bar(): string;
}
我有一些 class 扩展 Foo
:
class Foo1 extends Foo {
bar(): string { return 'foo1'; }
}
class Foo2 extends Foo {
bar(): string { return 'foo2'; }
}
我有另一个 class,我想将 Foo
的所有方法代理到 Foo
。如果我在此 class 上定义 Foo
的所有方法,这实际上工作正常。但我宁愿不那样做。我更愿意在 Foo
上定义 Foo
的方法,并且编译器知道 FooProxy
也实现了这些方法,而无需实际实现它们。这可能吗?代理 class 看起来像这样:
class FooProxy {
public foo: Foo;
constructor(foo: Foo) {
this.foo = foo;
let handler = {
get: function(target: FooProxy, prop: string, receiver: any) {
if(Foo.prototype[prop] !== null) {
return target.foo[prop];
}
return Reflect.get(target, prop, receiver);
}
}
return new Proxy(this, handler);
}
}
示例:
let f = new Foo1();
let fp = new FooProxy(f);
fp.bar();
输出:
error TS2339: Property 'bar' does not exist on type 'FooProxy'.
这个程序实际上在 playground 中运行,但是 tsc
没有发出任何东西。我只需要以某种方式欺骗编译器...
我不认为在这种情况下 class 是最好的方法,您可以只使用一个函数来创建代理,并且一切都会按预期工作:
function createFooProxy(foo:Foo) : Foo { // Proxy<Foo> is compatible with Foo
let handler = {
get: function(target: Foo, prop: keyof Foo, receiver: any) {
if(Foo.prototype[prop] !== null) {
return foo[prop];
}
return Reflect.get(target, prop, receiver);
}
}
return new Proxy(foo, handler);
}
如果您开始使用 class 方法,您可以伪造一个碱基 class:
function fakeBaseClass<T>() : new() => Pick<T, keyof T>{ // we use a pick to remove the abstract modifier
return class {} as any
}
class FooProxy extends fakeBaseClass<Foo>(){
private foo: Foo; // I would make this private as it is not really accessible on what the constructor of FooProxy returns (maybe remove it as I see no use for it)
constructor(foo: Foo) {
super();
this.foo = foo;
let handler = {
get: function(target: FooProxy, prop: keyof Foo, receiver: any) {
if(Foo.prototype[prop] !== null) {
return target.foo[prop];
}
return Reflect.get(target, prop, receiver);
}
}
return new Proxy(this, handler);
}
}
作为已接受答案的补充,我想分享一个解决方案,该解决方案可用于更简单的情况,在这些情况下,您只是希望能够代理任何方法或 属性(要么是陷阱处理程序或实际目标上实际存在的东西)。该解决方案基于使用“索引签名”,请在此处阅读有关该主题的更多信息:https://www.typescriptlang.org/docs/handbook/interfaces.html
我希望它对其他解决这个重要问题的人有用:
const handler = {
get: (target: object, property: string, receiver: any) => {
// define custom traps here:
if (property === "trap") {
// ... do something ...
}
// optional test whether property exists using the Reflect class:
if (!Reflect.has(target, property)) {
throw Error(`Property ${property} does not exist`);
}
// proxy to the target using the Reflect class:
return Reflect.get(target, property);
},
};
我将展示两个解决方案:
1.创建工厂和接口:
// Any property is allowed in this interface using the following index signature:
interface FooInterface {
[key: string]: any;
}
// From the factory we return the FooInterface
const proxyFactory = (target: Foo): FooInterface => {
return new Proxy(target, handler);
};
2. 直接从 class 构造函数
创建一个带有索引签名的 class 和 return 代理
class FooProxy {
[key: string]: any;
constructor(target: Foo) {
return new Proxy(target, handler);
}
}
可以像这样使用,其中所有可能的方法和属性都会被识别:
const fooProxy = proxyFactory(foo);
或
const fooProxy = new FooProxy(foo);
没有更多抱怨的消息:
Property '...property name...' does not exist on type 'Proxy'.
所以现在可以调用和捕获任何东西了:
const returnedFromMethod = fooProxy.anyMethod();
const property = fooProxy.anyProperty;
你当然可以细化你的接口,这个例子只是为了演示所有方法和属性的解决方案。
我有一个摘要class:
abstract class Foo {
abstract bar(): string;
}
我有一些 class 扩展 Foo
:
class Foo1 extends Foo {
bar(): string { return 'foo1'; }
}
class Foo2 extends Foo {
bar(): string { return 'foo2'; }
}
我有另一个 class,我想将 Foo
的所有方法代理到 Foo
。如果我在此 class 上定义 Foo
的所有方法,这实际上工作正常。但我宁愿不那样做。我更愿意在 Foo
上定义 Foo
的方法,并且编译器知道 FooProxy
也实现了这些方法,而无需实际实现它们。这可能吗?代理 class 看起来像这样:
class FooProxy {
public foo: Foo;
constructor(foo: Foo) {
this.foo = foo;
let handler = {
get: function(target: FooProxy, prop: string, receiver: any) {
if(Foo.prototype[prop] !== null) {
return target.foo[prop];
}
return Reflect.get(target, prop, receiver);
}
}
return new Proxy(this, handler);
}
}
示例:
let f = new Foo1();
let fp = new FooProxy(f);
fp.bar();
输出:
error TS2339: Property 'bar' does not exist on type 'FooProxy'.
这个程序实际上在 playground 中运行,但是 tsc
没有发出任何东西。我只需要以某种方式欺骗编译器...
我不认为在这种情况下 class 是最好的方法,您可以只使用一个函数来创建代理,并且一切都会按预期工作:
function createFooProxy(foo:Foo) : Foo { // Proxy<Foo> is compatible with Foo
let handler = {
get: function(target: Foo, prop: keyof Foo, receiver: any) {
if(Foo.prototype[prop] !== null) {
return foo[prop];
}
return Reflect.get(target, prop, receiver);
}
}
return new Proxy(foo, handler);
}
如果您开始使用 class 方法,您可以伪造一个碱基 class:
function fakeBaseClass<T>() : new() => Pick<T, keyof T>{ // we use a pick to remove the abstract modifier
return class {} as any
}
class FooProxy extends fakeBaseClass<Foo>(){
private foo: Foo; // I would make this private as it is not really accessible on what the constructor of FooProxy returns (maybe remove it as I see no use for it)
constructor(foo: Foo) {
super();
this.foo = foo;
let handler = {
get: function(target: FooProxy, prop: keyof Foo, receiver: any) {
if(Foo.prototype[prop] !== null) {
return target.foo[prop];
}
return Reflect.get(target, prop, receiver);
}
}
return new Proxy(this, handler);
}
}
作为已接受答案的补充,我想分享一个解决方案,该解决方案可用于更简单的情况,在这些情况下,您只是希望能够代理任何方法或 属性(要么是陷阱处理程序或实际目标上实际存在的东西)。该解决方案基于使用“索引签名”,请在此处阅读有关该主题的更多信息:https://www.typescriptlang.org/docs/handbook/interfaces.html
我希望它对其他解决这个重要问题的人有用:
const handler = {
get: (target: object, property: string, receiver: any) => {
// define custom traps here:
if (property === "trap") {
// ... do something ...
}
// optional test whether property exists using the Reflect class:
if (!Reflect.has(target, property)) {
throw Error(`Property ${property} does not exist`);
}
// proxy to the target using the Reflect class:
return Reflect.get(target, property);
},
};
我将展示两个解决方案:
1.创建工厂和接口:
// Any property is allowed in this interface using the following index signature:
interface FooInterface {
[key: string]: any;
}
// From the factory we return the FooInterface
const proxyFactory = (target: Foo): FooInterface => {
return new Proxy(target, handler);
};
2. 直接从 class 构造函数
创建一个带有索引签名的 class 和 return 代理class FooProxy {
[key: string]: any;
constructor(target: Foo) {
return new Proxy(target, handler);
}
}
可以像这样使用,其中所有可能的方法和属性都会被识别:
const fooProxy = proxyFactory(foo);
或
const fooProxy = new FooProxy(foo);
没有更多抱怨的消息:
Property '...property name...' does not exist on type 'Proxy'.
所以现在可以调用和捕获任何东西了:
const returnedFromMethod = fooProxy.anyMethod();
const property = fooProxy.anyProperty;
你当然可以细化你的接口,这个例子只是为了演示所有方法和属性的解决方案。