I'm trying to understand how the syntax in the following class is valid in TypeScript:
class Deferred<T> {
promise: Promise<T>;
resolve!: (value: T) => void;
reject!: (reason?: any) => void;
constructor() {
this.promise = new Promise<T>((resolve, reject) => {
this.resolve = resolve;
this.reject = reject;
});
}
}
Specifically, how the resolve
parameter is assignable to this.resolve
.... Since the signature of the Promise constructor is:
new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void) => Promise<T>
but the resolve
field on the class is only (value: T) => void
. Since the constructor defines that with a union type of also including PromiseLike<T>
it would seem that this assignment shouldn't be allowed but it seems to compile work fine. Any explanation for why this is?
CodePudding user response:
Contravariance of functions is hard to get your head around.
Assume these two types (named after where they appear in your code);
type ResolveValue = (value: T | PromiseLike<T>) => void
type ResolveField = (value: T) => void
Now, you're wondering why
const value : ResolveValue = ...;
const field : ResolveField = value; // Why no compiler error?
is legal. Here's why:
When the compiler says
Give me a function that I can call with a
T
and you say
Can I give you a function that you can call with a
T
or aPromiseLike<T>
?
then the compiler says
Sure, that's fine. I'll just never call it with the latter.
and happily assigns your value
to field
, because it can.
CodePudding user response:
The signature (value: T) => void
is a valid target for (value: T | PromiseLike<T>)
, as it matches the wider signature (they overlap). The other way around would not work.