Home > Blockchain >  Typescript Promise resolve syntax with narrowing type
Typescript Promise resolve syntax with narrowing type

Time:10-25

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 a PromiseLike<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.

  • Related