Demo code in the TypeScript playground
I'm trying to create a more dynamic implementation of the decorator pattern using the ES6's Proxy
object .
The general idea is that I have an IService
interface and a BaseService
abstract class. The abstract class has an origin
property that's an instance of IService
and all the unimplemented calls are forwarded to this object. BaseService
exposes a static method wrap
that creates a new instance of the decorator and sets it's origin to the service instance provided as a parameter.
Functionality of this approach is not a problem, the problem comes when I try to specify the types. For some reason, when I try to decorate the MongoService
with a VerificationService
, the origin type is reduced to IService
and the return value is of type VerificationService & IService
instead of the desired VerificationService & MongoService
.
My question is whether this is intended behavior because of some covariance/contra-variance issues the code could lead to or if it's a bug and the compiler just doesn't know that the type of the parameter is MongoService. Please notice that when I try to decorate an instance of a class that doesn't add any properties, the type is correctly inferred (line 37)
I am not 100% sure why this happens, but if you look at the type of wrapper
and the constructor for VerificationService
(inherited from BaseService
) we get an idea of why this might be. The parameter to the constructor is ISerivce
not a generic type, so the simplest assumption would be to infer U
to IService
in wrap
, and the second parameter to wrap
satisfies IService
so it all works fine, no reason to look further.
The simplest solution would be to change the type of wrapper
, since the argument can be any service anyway:
static wrap<U extends IService, T extends BaseService>(this: { new(origin: IService): T }, origin: U): T & U {
return new Proxy(new this(origin) as T & U, {
get(target: T & U, prop: keyof T | keyof U) {
if (isOverriden(target, prop)) return target[prop];
return target.origin[prop];
}
})
}
// All work as expected
const serviceInferedIncorrectly = VerificationService.wrap(new MongoService()); // VerificationService & MongoService
const serviceInferedCorrectly = VerificationService.wrap(new SimpleService()); // VerificationService & SimpleService
const serviceSpecified = VerificationService.wrap<MongoService, VerificationService>(new MongoService()); // VerificationService & MongoService
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.