Issue
I would like to have a type safe register function that registers a WorkerOf with two arguments and two generic types so that consumers have to register a ClassA with the corresponding WorkerOfA.
type Worker<Payload> = { exec: (payload: Payload) => Promise<void> };
type Constructor<Payload> = new (...args: any[]) => Payload;
type WorkerConstructor<Payload extends Constructor<Payload>> = new (
...args: any[]
) => Worker<Payload>;
function register<Payload extends Constructor<Payload>>(
_pyd: Payload,
_wkr: WorkerConstructor<Payload>
) {}
class PayloadOne {}
class PayloadTwo {}
class WorkerOne implements Worker<PayloadOne> {
constructor() {}
async exec(_payload: PayloadOne): Promise<void> {}
}
// Expected to work because WorkerOne implements Worker<PayloadOne>
register(PayloadOne, WorkerOne);
// Expected to fail because WorkerOne doesn't implements Worker<PayloadTwo>
register(PayloadTwo, WorkerOne);
In both cases I am getting the following typescript error:
Argument of type 'typeof PayloadOne' is not assignable to parameter of type 'Constructor<typeof PayloadOne>'. Property 'prototype' is missing in type 'PayloadOne' but required in type 'typeof PayloadOne'.ts(2345)
Solution
Your generic constraints of the form T extends Constructor<T> don't mean what you want them to mean. If you need T to be a constructor type, T extends Constructor<any> would be sufficient. Otherwise, T extends Constructor<T> means that T is a constructor type whose instances are themselves T. So it's a constructor that constructs constructors that construct constructors that construct constructors that... 😵.
For the example you present, you don't even need T extends Constructor<any>. Instead, let's let T be an arbitrary type, and have register()'s _pyd parameter be of type Constructor<T>, while _wkr is of type WorkerConstructor<T>. That is, we would like the following code to be well-typed:
new _wkr().exec(new _pyd())
It looks like this:
type Worker<T> = { exec: (payload: T) => Promise<void> };
type Constructor<T> = new (...args: any[]) => T;
type WorkerConstructor<T> = new (...args: any[]) => Worker<T>;
function register<T>(
_pyd: Constructor<T>,
_wkr: WorkerConstructor<T>
) { }
That all compiles fine, and note that T is not constrained anywhere. Let's make sure it works when you call register():
class PayloadOne { a = 1 }
class PayloadTwo { b = 2 }
class WorkerOne implements Worker<PayloadOne> {
constructor() { }
async exec(_payload: PayloadOne): Promise<void> { }
}
register(PayloadOne, WorkerOne); // okay
register(PayloadTwo, WorkerOne); // error
// Argument of type 'typeof PayloadTwo' is not assignable
// to parameter of type 'Constructor<PayloadOne>
Looks good!
Answered By - jcalz
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.