1

I want to extend the PromiseLike interface.

(I use TypeScript v3.6.3)

I took the original PromiseLike interface:

interface PromiseLike<T> {
    then<TResult1 = T, TResult2 = never>(
        onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
        onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null,
    ): PromiseLike<TResult1 | TResult2>
}

then I only inherit PromiseLike and replace nested PromiseLike types to Thenable:

interface Thenable<T = any> extends PromiseLike<T> {
    then<TResult1 = T, TResult2 = never>(
        onfulfilled?: ((value: T) => TResult1 | Thenable<TResult1>) | undefined | null,
        onrejected?: ((reason: any) => TResult2 | Thenable<TResult2>) | undefined | null,
    ): Thenable<TResult1 | TResult2>
}

and got this error:

Error:(28, 18) TS2430: Interface 'Thenable<T>' incorrectly extends interface 'PromiseLike<T>'.
  Types of property 'then' are incompatible.
    Type '<TResult1 = T, TResult2 = never>(onfulfilled?: (value: T) => TResult1 | Thenable<TResult1>, onrejected?: (reason: any) => TResult2 | Thenable<TResult2>) => Thenable<TResult1 | TResult2>' is not assignable to type '<TResult1 = T, TResult2 = never>(onfulfilled?: (value: T) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => PromiseLike<TResult1 | TResult2>'.
      Types of parameters 'onrejected' and 'onrejected' are incompatible.
        Type 'TResult2 | PromiseLike<TResult2>' is not assignable to type 'TResult2 | Thenable<TResult2>'.
          Type 'PromiseLike<TResult2>' is not assignable to type 'TResult2 | Thenable<TResult2>'.
            Type 'PromiseLike<TResult2>' is not assignable to type 'Thenable<TResult2>'.
              Types of property 'then' are incompatible.
                Type '<TResult1 = TResult2, TResult2 = never>(onfulfilled?: (value: TResult2) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => PromiseLike<TResult1 | TResult2>' is not assignable to type '<TResult1 = TResult2, TResult2 = never>(onfulfilled?: (value: TResult2) => TResult1 | Thenable<TResult1>, onrejected?: (reason: any) => TResult2 | Thenable<TResult2>) => Thenable<TResult1 | TResult2>'.
                  Types of parameters 'onrejected' and 'onrejected' are incompatible.
                    Type 'TResult2 | Thenable<TResult2>' is not assignable to type 'TResult2 | PromiseLike<TResult2>'.
                      Type 'Thenable<TResult2>' is not assignable to type 'TResult2 | PromiseLike<TResult2>'.
                        Type 'Thenable<TResult2>' is not assignable to type 'PromiseLike<TResult2>'.
                          Types of property 'then' are incompatible.
                            Type '<TResult1 = TResult2, TResult2 = never>(onfulfilled?: (value: TResult2) => TResult1 | Thenable<TResult1>, onrejected?: (reason: any) => TResult2 | Thenable<TResult2>) => Thenable<TResult1 | TResult2>' is not assignable to type '<TResult1 = TResult2, TResult2 = never>(onfulfilled?: (value: TResult2) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => PromiseLike<TResult1 | TResult2>'.
                              Types of parameters 'onrejected' and 'onrejected' are incompatible.
                                Type 'TResult2 | PromiseLike<TResult2>' is not assignable to type 'TResult2 | Thenable<TResult2>'.
                                  Type 'PromiseLike<TResult2>' is not assignable to type 'TResult2 | Thenable<TResult2>'.

How to fix this error?

Further, I would like to extend this interface like this:

export interface Thenable<T = any> extends PromiseLike<T> {
    then<TResult1 = T, TResult2 = never>(
        onfulfilled?: ((value: T) => TResult1 | ThenableOrIteratorOrValue<TResult1>) | undefined | null,
        onrejected?: ((reason: any) => TResult2 | ThenableOrIteratorOrValue<TResult2>) | undefined | null,
    ): ThenableOrIteratorOrValue<TResult1 | TResult2>
}

and I want it to remain compatible with PromiseLike, so that async await functions are compatible with it.

Nikolay Makhonin
  • 1,087
  • 12
  • 18
  • You cannot extend an interface and overwrite a property. Please see https://stackoverflow.com/questions/41285211/overriding-interface-property-type-defined-in-typescript-d-ts-file – a1300 Sep 30 '19 at 12:58
  • Why do you want to extend the PromiseLike interface? What do you try to achieve? Thanks – a1300 Sep 30 '19 at 12:59
  • I implemented synchronous version of async functions using generator (yield instead await). I use Iterator type as synchronous Promise. And now I want to combine all promise like interfaces. – Nikolay Makhonin Sep 30 '19 at 13:12
  • Would a `union` type be of helpful? I don't know your code for your `ThenableOrIteratorOrValue`. Maybe something like the following could help you: `type PromiseLikeOrThenable = PromiseLike | Thenable | IteratorResult;` – a1300 Sep 30 '19 at 13:32

1 Answers1

1

Remaining compatible with PromiseLike means that your Thenable could be passed as a parameter to a code that expects PromiseLike, and that code still should work. For that to work, then should accept onfulfilled callback which returns PromiseLike, not your Thenable - because that's the type of callback that the code that uses PromiseLike will pass to your implementation of then.

So, adding PromiseLike to a union of types that could be returned by the callback like this makes the error go away:

    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,

For reasons that I don't understand, onrejected is compatible as is, apparently because it's type is not dependent on generic parameter T.

artem
  • 46,476
  • 8
  • 74
  • 78