Definitelytyped: bluebird 3.0 definifion is not assignable to ES6 Promises

Created on 5 Sep 2016  ·  48Comments  ·  Source: DefinitelyTyped/DefinitelyTyped

The bluebird 3.0 definition is not assignable to standard es6 Promise definition:

Types of property 'then' are incompatible.
Type 'Bluebird<any>' is not assignable to type 'Promise<any>'.

/cc @lhecker

Most helpful comment

@silentorb I have got this working by using the @types/bluebird-global type definition and then overriding the global promise definition (which you'll have if you're targeting an ES2015 platform) by adding the following to the top of my code execution entrypoint:

import * as Promise from 'bluebird';
global.Promise = require('bluebird');

The above works for node environment (I'm using 6.10.x). If you're using webpack then you may need to use something like _expose-loader_.

All 48 comments

@Strate Can you post the remaining output of tsc? And please try to open your local bluebird.d.ts and add this single line from #10831. Does that fix your issue?

Whole error:

error TS2322: Type '(entity: BaseEntity) => Bluebird<{ contentType: "ActivityDesktopWidget" | "AddressType" | "Approv...' is not assignable to type 'UpdateEntityFunction<BaseEntity>'.
  Type 'Bluebird<{ contentType: "ActivityDesktopWidget" | "AddressType" | "Approval" | "BaseIntegrationEn...' is not assignable to type 'Promise<BaseEntity>'.
    Types of property 'then' are incompatible.
      Type '{ <U>(onFulfill: (value: { contentType: "ActivityDesktopWidget" | "AddressType" | "Approval" | "B...' is not assignable to type '{ <TResult1, TResult2>(onfulfilled: (value: BaseEntity) => TResult1 | PromiseLike<TResult1>, onre...'.
        Type 'Bluebird<any>' is not assignable to type 'Promise<any>'.

I am using [email protected] with lib.es2016.d.ts file. Adding that line does not help.

Ah I see... I believe it's due to this line missing here. This means with the current typings you have to return the same type U from both onFulfill as well as onReject. It'd be super cool if someone could fix this in the npm and this dt repository btw.

In the meantime you should probably split up your .then(success, failure) into .then(success).catch(failure) since that is probably what you did right? While it's part of the official syntax it's really not the "preferred" one with bluebird anyways though.

The alternative is that you could manually specify the generic parameter U like this

.then<SomeType>(() => new SomeType(), (err) => 123); // This is an explicit error because SomeType != number

to ensure that you return the same type from both callbacks.

@lhecker but I don't even use then or catch. I have something like that:

// module 1. Note that bluebird is not imported
export default function<T>(promise: Promise<T>) {} // es6 promise used here

// module 2.
import Promise from "bluebird"
import handler from "./module1"
const promise: Promise<any>
handler(promise) // <-- error is here

Seems that it could be fixed by adding yet another then declaration, compatible with es6's one to bluebird.d.ts

Ah damn... sorry. I guess I should finally take a break. 😅

So ignore my comments about what to do in the meantime: Since they are missing anyways and since I really don't or at least "should not" have the time for doing anything besides basic support like this, it'd be greatly appreciated if you could send PRs to add those typings to both projects. 😊

@lhecker done :)

Just a side note, but I always recommend using PromiseLike for when you accept promises. It will guarantee the minimum implemented promise interface so you can accept most promises. Then you can return the promise type of your chosing, making it stricter like Bluebird<T> which has many extra methods. For a long while, none of my promises were assignable because of the ES6 symbols being added to ES6 promise types.

@blakeembrey using PromiseLike in this case is not the answer, because PromiseLike also has incompatible version of then

Unfortunately the Promise typings have changed in TS version 2 and so these typings no longer correspond: https://github.com/Microsoft/TypeScript/blob/070aa83cc06b2974639bbefcde98e6e2fb5fe693/src/lib/es2015.promise.d.ts

Can we re-open this issue?

Any updates on this?

@OliverJAsh @arg20 could you guys provide self-reproduceable test case of your issue?

@Strate here you have my error. Using typescript 2.0.3 (lib.es6.d.ts) and @types/bluebird v3.0.33

error TS2345: Argument of type 'Bluebird<Db>' is not assignable to parameter of type 'Promise
<Db>'.                                                                                                            
  Types of property 'then' are incompatible.                                                                      
    Type '{ <U1, U2>(onFulfill: (value: Db) => U1 | Thenable<U1>, onReject: (error: any) => U2 | Thenable<U...' is
 not assignable to type '{ <TResult1, TResult2>(onfulfilled: (value: Db) => TResult1 | PromiseLike<TResult1>, onre
jected: ...'.                                                                                                     
      Type 'Bluebird<any>' is not assignable to type 'Promise<any>'.                                              
        Types of property 'then' are incompatible.                                                                
          Type '{ <U1, U2>(onFulfill: (value: any) => U1 | Thenable<U1>, onReject: (error: any) => U2 | Thenable<.
..' is not assignable to type '{ <TResult1, TResult2>(onfulfilled: (value: any) => TResult1 | PromiseLike<TResult1
>, onrejected:...'.                                                                                               
            Type 'Bluebird<any>' is not assignable to type 'Promise<any>'.              

Trying to cast the Bluebird Promise to an ES6 promise throws the following (bluebirdPromise as Promise<Db>)

 error TS2352: Type 'Bluebird<Db>' cannot be converted to type 'Promise<Db>'.                 
  Types of property 'then' are incompatible.                                                                      
    Type '{ <U1, U2>(onFulfill: (value: Db) => U1 | Thenable<U1>, onReject: (error: any) => U2 | Thenable<U...' is  not comparable to type '{ <TResult1, TResult2>(onfulfilled: (value: Db) => TResult1 | PromiseLike<TResult1>, onrejected: ...'.                                                                                                     
      Type 'Bluebird<any>' is not comparable to type 'Promise<any>'.                                              
        Property '[Symbol.toStringTag]' is missing in type 'Bluebird<any>'.         

adding this as proposed in #10831 worked for me

readonly [Symbol.toStringTag]: 'Promise';

also, converting to a ES6 Promise did the trick

return new Promise.resolve(bluebirdPromise)

@jmendiara as stated in #10831, there is no readonly [Symbol.toStringTag] in bluebird actually, so, adding this to bluebird.d.ts is definetely wrong: typings should represent real world.
If Promise standard require readonly [Symbol.toStringTag], it should be added to bluebird itself and bluebird.d.ts too. Seems that you should definetely use conversion between bluebird and native promises (which is really annoying).
FYI: you can use Promise.resolve without new keyword.

Just ran into issue when bluebird's promise is not assignable to standard one. And in my case it is impossible to cast bluebird to standard with Promise.resolve, because it is deep in 3rd party object. So, sometimes it is reasonable to have bluebird's promise to be assignable to standard one without conversion.
Just created feature request in bluebird's repository, to add Symbol.toStringTag to bluebird's instances.

https://github.com/petkaantonov/bluebird/issues/1277

Any updates on this?

Glad I found this thread. Thought I was going nuts. Would really like to see a fix for this. I described my problem specifics on SO

I'm running into this issue with @types/bluebird 3.5.3 and TypeScript 2.2.2.

@silentorb I have got this working by using the @types/bluebird-global type definition and then overriding the global promise definition (which you'll have if you're targeting an ES2015 platform) by adding the following to the top of my code execution entrypoint:

import * as Promise from 'bluebird';
global.Promise = require('bluebird');

The above works for node environment (I'm using 6.10.x). If you're using webpack then you may need to use something like _expose-loader_.

@ksnyde: I previously tried out @types/bluebird-global and ran into multiple missing Promise details. I considered patching @types/bluebird-global but it would be better to have @types/bluebird working.

@silentorb the key is to override the global Promise reference; works without issue for me. That said, it certainly would be nice if it just worked out of the box but there's no need to wait with this solution.

Any progress on this?

I'm also getting this issue. It boils down to errors like the following:

type Promise<any> is not assignable to Bluebird<any>

Very frustrating. This is also breaking other typing packages' usages, such as sequelize.

export interface MissionModel extends Sequelize.Model<MissionInstance, MissionAttributes. {
    create(missionAttributes: MissionAttributes, opsions?: Sequelize.CreateOptions): Promise<MissionInstance>;
}

MissionModel will have the following error unless Promise is imported as * from "bluebird":

src\server\models\mission.ts(3,18): error TS2430: Interface 'MissionModel' incorrectly extends interface 'Model<MissionInstance, MissionAttributes>'.
  Types of property 'create' are incompatible.
    Type '(MissionAttributes: MissionAttributes, option?: CreateOptions | undefined) => Promise<MissionInst...' is not assignable to type '(values?: MissionAttributes | undefined, options?: CreateOptions | undefined) => Bluebird<Mission...'.
      Type 'Promise<MissionInstance>' is not assignable to type 'Bluebird<MissionInstance>'.
        Types of property 'then' are incompatible.
          Type '<TResult1 = MissionInstance, TResult2 = never>(onfulfilled?: ((value: MissionInstance) => TResult...' is not assignable to type '{ <U>(onFulfill?: ((value: MissionInstance) => U | PromiseLike<U>) | undefined, onReject?: ((erro...'.
            Type 'Promise<any>' is not assignable to type 'Bluebird<any>'.

@Strate could you please re-open the issue?
On second thought, I'll just file a new, more-targeted one.

I really hate wasting time to fix this kind of typescript problem. Because the code is very clear, but to pass tsc you have to do more extra work. The easy way I fix it is make a temp variable with type any. Then convert it to your target Promise. Such as:

'''
const p: any = getPromise();
return >p;
'''

@flyingsky (et. al.) First of all I'd like to remind everyone again that it will never ever be possible to directly assign native Promises to Bluebird ones, since Bluebird promises provide extensions which do not exist in the native implementation.

Assigning Bluebird promises to native ones on the other hand should be possible and rightly so. But many here are under the impression that this should just work right now, which definitely is not the case.

The reason for this is that while Bluebird exposes all commonly known functions in a native compatible way, it _does not_ expose one particular field: The [Symbol.toStringTag] field has to be "promise". See here: https://github.com/petkaantonov/bluebird/issues/1277. This makes Bluebird strictly seen incompatible with native promises. Soo...

const p: any = getPromise();
return <Promise>p;

This is technically type unsafe and incorrect.

I considered patching @types/bluebird-global, but it would be better to have @types/bluebird working.

bluebird-global serves an entirely different use case than bluebird does: It exists in case you go the very much type unsafe route of overwriting the global Promise variable with Bluebird. (It's unsafe because simply overwriting it does not mean that every package you depend on won't continue using native promises.)

If anyone in this issue wants to see progress I kindly suggest opening a PR for the already mentioned Bluebird issue: https://github.com/petkaantonov/bluebird/issues/1277

If anyone in this issue wants to see progress I kindly suggest opening a PR for the already mentioned Bluebird issue: petkaantonov/bluebird#1277

Looks like this PR has been created: https://github.com/petkaantonov/bluebird/pull/1421

@ksnyde – Your @types/bluebird-global suggestion works for me, slightly modified:

import * as Promise from 'bluebird'
global.Promise = Promise

Otherwise I get error TS6133: 'Promise' is declared but its value is never read. Which is expected for tsconfig.json with "noUnusedLocals": true.

Thank you.

I'm using bluebird-global but still get this error:

Property '[Symbol.toStringTag]' is missing in type 'Bluebird'

Adding this line fixes the issue https://github.com/DefinitelyTyped/DefinitelyTyped/pull/10831/files

Is there any way to fix it locally without modifying bluebird's typings?

+1

@gdpaulmil Thanks for your constructive comment!

It prompted me to take a look at the state of affairs over at Bluebird.
Turns out https://github.com/petkaantonov/bluebird/pull/1421 was merged 11 days ago, meaning they finally added support for Symbol.toStringTag! 🎉

This in turn means this problem can be fixed right away by resubmitting #10831. One will only have to swap out "Promise" with "Object". I'd honestly review it right away. 🙂

@lhecker, created #35353 to get this all cleared up!

Hey, I'd just like to let you all know that this issue has been fixed in v3.5.27 of @types/bluebird.
You may now enjoy being able to assign all these Bluebirds to all these Promises. 🎉
You'll have to use TypeScript 3.2 or newer for this though. (See #34805)

P.S.: Thanks again @JoshuaKGoldberg for dealing with the Bluebird PR. 🙂👍

Is:

import * as Promise from 'bluebird';
global.Promise = require('bluebird');

Still applicable for es2017?

Except for all the other packages that have the previous version as a dependency and didn't get the memo... request-promise, knex,... you just broke a lot of code with this update. The Typescript compiler is throwing nonsense errors all over the place, Typescript 3.3, 3.4, 3.5... doesn't seem to matter.

If it helps anyone, as mentioned higher up I had the easiest time just NOT swapping my global Promise object out for BB. I haven't encountered any issues where one thing was compatible with another thing.

@jacklinton I'm sure you are already aware that unit tests are in place to verify that all affected packages still build with the given TypeScript version without "throwing nonsense errors".
For instance here's a project that shows request-promise working with the newest Bluebird typings: https://github.com/lhecker/request-promise-sample
Since I can't reproduce your issue it'd be awesome if you could provide a minimal example which shows the issues you're seeing.

It's difficult to know where to begin, but with all other packages up to date
Error:(19, 3) TS2741: Property '[Symbol.toStringTag]' is missing in type 'Bluebird<string[]>' but required in type 'Bluebird<string[]>'.
If I remove @types/bluebird these errors go away. The only conjecture I can make with the time I have is that not all other packages that need these types have caught up yet. I Suspect Knex is a likely culprit, but I don't know. It grabs its own dependency of the previous version of this package.

This error does sound as if you got two versions of @types/bluebird installed. You should try and see whether you can flatten that tree so that you only have one of them.

knex 0.17.3 depends on the newest 3.5.27 version of the @types/bluebird typings btw. If you have an up-to-date version of knex it should actually work I suppose.
Same goes for your your second copy of @types/bluebird though: It should also be 3.5.27.

Thanks for being responsive @lhecker . I didn't mean to sound critical, I apologize. You've clearly been working hard on this and it's something we've all been waiting a long time for. Thanks for your hard work getting this update out so quickly after Bluebird made the change. I'll get this bloated monolith to work the way it's supposed to one way or another.

@lhecker can you please republish your request-promise-sample because it seems to be gone. 😢

@VictorioBerra the trick with

import * as Promise from 'bluebird';
global.Promise = require('bluebird');

gives me to following error on Node.js 12:

global.Promise = Promise;
Type 'typeof Bluebird' is missing the following properties from type 'Function': apply, call, bind, length, and 4 more.

I am using TypeScript v3.7.4, bluebird v3.7.2 and @types/bluebird-global v3.5.12.

@bennyn I don't have the code anymore. I'm very sorry. I deleted it after the request-promise issue was fixed.
Make sure you use the latest version of @types/bluebird at 3.5.29.

I'm having the same error as @bennyn , with the latest versions of all bluebird, @types/bluebird-global and TypeScript

It seems the only viable option with current version of Bluebird 3.7.2 and TypeScript 3.6 is not to use Bluebird typings at all.

At the app entrypoint I replace native Promise constructor with Bluebird and use Bluebird using native promise API after that:

import * as Bluebird from "bluebird";
global.Promise = Bluebird;

new Promise((resolve, reject) => {
  // this is actually a Bluebird object
});

I personally, frankly, believe that you guys are doing something wrong on your end. 😖
I've reconstructed my minimal request-promise-sample project which shows how you absolutely 100% definitely can...

  • assign the Bluebird constructor to global.Promise
  • use request-promise without compilation errors
  • assign Bluebird class instances to the Promise interface (which is what this issue is about)

_That said, this issue has been closed and should not be used for continued off-topic discussions._
The above discussion is off-topic, as this ticket is about Bluebird promises not being assignable to ES6/native Promises. But as shown above this issue has long been fixed.
If you have trouble using @types/[email protected] with Bluebird please see https://github.com/DefinitelyTyped/DefinitelyTyped/issues/42084 instead.

@lhecker
Thank you for the reply. I tried your project and it also doesn't work:
Annotation 2020-03-21 084422

Installing @types/[email protected] doesn't help either:
Annotation 2020-03-21 084750

Modified project can be found here:
https://github.com/chebum/request-promise-sample

@chebum Your comment above didn't make clear that you're trying to type async functions as ones returning a Bluebird<T>. In that case I could've already told you that this isn't possible unfortunately. As you can see by the output of your compiler you _must_ return a Promise<T> type from an async function.

If you'd like to waive a little bit of your type safety you can do the following though:

  1. Add @types/bluebird-global as dependency
  2. Override the global Promise constructor: window.Promise = Bluebird as any;
  3. Write: async function testFn(): Promise<void>
  4. The global Promise<T> type is now almost identical to Bluebird<T> and you should be able to use all important Bluebird features.

If you hit any Symbol.species errors etc. please _do not_ use this ticket but rather #42084.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Loghorn picture Loghorn  ·  3Comments

tyv picture tyv  ·  3Comments

JudeAlquiza picture JudeAlquiza  ·  3Comments

fasatrix picture fasatrix  ·  3Comments

jbreckmckye picture jbreckmckye  ·  3Comments