Definitelytyped: bluebird 3.0 : comment l'utiliser comme surcharge pour global Promise ?

Créé le 24 août 2016  ·  44Commentaires  ·  Source: DefinitelyTyped/DefinitelyTyped

Salut tout le monde!

J'utilise bluebird en remplacement de l'objet global Promise. J'ai essayé de mettre à jour les typedefs vers la dernière version, publiée par @lhecker , et j'ai rencontré un problème: global Promsie n'est pas surchargé maintenant par défaut.
Comment puis-je atteindre le comportement précédent ? Peut-être pourrions-nous avoir bluebird-global.d.ts, par exemple ?

Commentaire le plus utile

Salut les gars,

@types/bluebird-global est maintenant disponible. Ces typages utilisent @types/bluebird@^3.0 sous le capot et vous permettent d'utiliser les méthodes de bluebird sur le Promise global (c'est-à-dire que la compilation ts n'échoue pas).

Veuillez lire ceci pour voir comment l'utiliser.

Tous les 44 commentaires

Eh bien, à mon humble avis, les définitions précédentes de Bluebird n'étaient pas non plus une bonne solution, car elles fuyaient fortement dans l'espace de noms global et je pense que c'était une bonne idée de réduire les efforts redondants.

La façon dont les définitions précédentes fonctionnaient cependant consistait à définir declare var Promise: PromiseConstructor; tandis que PromiseConstructor était l'interface Bluebird précédente (définie globalement).

C'est-à-dire que si vous créez un fichier local *.d.ts et que vous ajoutez quelque chose comme ça, cela pourrait peut-être fonctionner ?

import Bluebird = require("bluebird");
declare var Promise: Bluebird<any>;

ça peut marcher peut-être ?

Malheureusement non. Parce que j'ai beaucoup de code, qui écrit comme ceci:

declare function doLoadData(): Promise<Data>

comme vous pouvez le voir, la fonction renvoie Promise<T> , qui est standard Promsie , pas bluebird. en déclarant var Promise: Bluebird<any> je vais surcharger le constructeur Promise standard, pas l'interface.

Je suis revenu aux typages 2.0 pour cette raison.

@Strate Ah putain j'ai écrit un long commentaire sur ce que je pense à ce sujet et sur ce que nous devrions essayer de faire. Mais il semble que j'ai oublié de le soumettre et qu'il a finalement été perdu...

Beaucoup de gens semblent avoir ce problème.

J'ai créé un dépôt qui montre le problème : https://github.com/d-ph/typescript-bluebird-as-global-promise

git clone && npm install && npm run tsc

Le problème:
Les fichiers tiers d.ts sont typés par rapport à Promise . Ce Promise est soit défini par lib.es6.d.ts de typescript (lorsque "target": "es6" ) soit par d'autres bibliothèques, par exemple core-js (très populaire, lors de la compilation vers es5 avec typescript) . Le dernier bluebird.d.ts n'est pas déclaré comme global Promise , même si bluebird.js s'expose comme global Promise .

Résultat:
Les développeurs ne peuvent pas utiliser la fonctionnalité de bluebird sur les promesses renvoyées par le code tiers (la compilation échoue).

Résultat attendu:
Les développeurs peuvent utiliser la fonctionnalité de bluebird sur les promesses renvoyées par le code tiers (la compilation réussit).

Je ne sais pas qui est le mainteneur des typages bluebird. @lhecker , vous êtes la personne malchanceuse à être retournée par git blame . Pourriez-vous nous dire, quelle est la raison préférée d'utiliser les typages bluebird de telle manière que le projet github, que j'ai lié ci-dessus, compile?

Contournements actuels :

  1. Le plus sale. N'importez jamais bluebird et n'utilisez jamais les typages bluebird. Pour les fonctions bluebird Promise , utilisez ceci : Promise["config"]({}); Promise.resolve("foo")["finally"](() => { console.log("lol"); }) pour mettre le compilateur en mode silencieux. C'est-à-dire utiliser l'opérateur d'accès au tableau : [""]
  2. Sale et ennuyeux. Dans chaque fichier ts d'entrée de votre application, ajoutez ces deux lignes :
import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }

Pour les fonctions statiques de Bluebird, utilisez Bluebird.config({}) au lieu de Promise.config({}) .

L'inconvénient est que les IDE ont du mal à interpréter correctement ce hack.

Hmm... C'était un peu imprévu pour moi que vous ayez des problèmes avec ça, puisque vous utilisez tous Bluebird d'une manière que moi et beaucoup d'autres n'utilisent pas. En fait, je n'ai même pas écrit les dactylographies moi-même ! J'ai simplement copié les seuls existants pour 3.0 à partir de here , car avoir des typages pour 3.0 vaut mieux que n'en avoir aucun, n'est-ce pas?

Le fait est que la direction actuelle de TypeScript est clairement modules > globals , ce que j'approuve vraiment. Mais cela signifie également que les modules ne doivent jamais modifier les objets globaux, surtout si vous considérez que Bluebird ne remplace pas le global Promise dans tous les cas ! Ou pour le dire ainsi :

Qu'advient-il de votre "sécurité de type" si vous appelez littéralement Promise.noConflict() n'importe où dans votre code ? Il rétablira le type global Promise à celui d'origine et fera planter votre code, même si tsc vous a dit que tout allait bien.

Alors oui... @d-ph. Votre deuxième solution est ce que vous auriez dû envisager de faire depuis le début, car c'est dans l'esprit des systèmes de modules. Mais je sais que ce n'est que la solution idéale pour les bibliothèques, alors que cela _peut_ être vraiment ennuyeux pour les applications. Je suis d'accord que les systèmes d'application devraient au moins pouvoir remplacer l'objet global Promise et avoir également les typages correspondants pour ce cas d'utilisation tel qu'il était disponible dans la version 2.0.

En fin de compte, je pense qu'à la lumière de l'idéologie de TypeScript, l'extension du type global Promise devrait être faite _très_ soigneusement (rappelez-vous le problème noConflict() etc.) et si c'est le cas uniquement en tant qu'opt-in.

IMO, la voie à suivre consiste à écrire un fichier bluebird-global.d.ts (ou similaire) qui étend l'objet global Promise avec les mêmes déclarations d'interface trouvées dans le fichier bluebird.d.ts . Et si vous avez besoin de les utiliser, vous devriez les importer explicitement au lieu de les avoir toujours inclus. De cette façon, vous pouvez avoir des typages sûrs _et_ corrects pour la plupart des cas d'utilisation et en particulier lors de l'écriture de bibliothèques, tout en ayant accès aux avantages supplémentaires d'écraser le Promise global dans les applications.

Si vous pensez que cette idée est bonne et qu'il vous reste du temps libre, ce serait cool si vous pouviez créer un PR. Je suis sûr que beaucoup seraient vraiment heureux d'une telle contribution. 🙂

Je le formule de cette façon parce que je ne suis actuellement pas en mesure d'écrire ces typages, par manque de temps et de ne pas avoir besoin de tels typages pour le moment. J'espère que vous pouvez comprendre cela.

@lhecker Je pense que je pourrais être d'accord avec vous. Parce que si nous avons un remplacement global de Promise par celui de Bluebird, nous ne piraterons que le compilateur TypeScript, mais pas le monde réel. Par exemple, avec le script dactylographié Promise remplacé, on pensera que fetch renvoie celui de bluebird :

import `whatwg-fetch`;
let result = fetch("anyurl"); // this is NOT bluebird promise, but typescript think that it is.

Sans encapsuler fetch dans le Promise.resolve de bluebird, vous n'obtiendrez pas, par exemple, la méthode .finally sur result :

import `whatwg-fetch`;
fetch("anyurl").then().finally() // goes to runtime error with overriden global promise, but should be compile error.

Donc, je pense qu'importer explicitement bluebird à chaque utilisation est une meilleure solution :

import Promise from "bluebird";
import `whatwg-fetch`;
Promise.resolve(fetch("anyurl")).then().catch() // no compile error, no runtime error

Je vais refactoriser mon code.

Merci pour votre réponse, @lhecker .

Je suis d'accord avec tout ce que vous avez dit. Et j'aime la solution de @Strate consistant à envelopper le code d'un tiers avec la méthode Promise.resolve() , pour transformer la promesse es6 en Bluebird (ou Bluebird en Bluebird, parce que je veux garder la promesse de Bluebird globale dans le runtime, donc je ne pas besoin de s'appuyer sur un code tiers pour gérer correctement leurs erreurs, mais ce n'est pas la question).

Il semble que je ne savais pas comment faire cela correctement. Ce dont je pense que d'autres pourraient bénéficier, c'est plus de documentation sur la façon de traiter ce problème, car tout le monde venant du monde de la programmation de navigateur (par opposition à : de nodejs/typescript) le frappe après :

  1. npm install <absolutely everything that uses es6 promise>
  2. npm install bluebird @types/bluebird
  3. utiliser le code tiers avec tapuscrit

Je bénéficierais également d'avoir cette section "Comment utiliser dans le cas où le code tiers est tapé contre es6 Promise" documentée quelque part pour référence future. c'est-à-dire avoir le

import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }

et

import * as Promise from 'bluebird';
import { Observable } from "rxjs";

let observable = Promise.resolve(new Observable<number>().toPromise());

dans un fichier readme ou dans le bloc doc en haut du fichier bluebird.d.ts.

Qu'en penses-tu?

J'ai terminé de passer du remplacement global de Promise à ceux de bluebird, et j'ai trouvé quelques problèmes, où les bibliothèques tierces renvoient ES6 Promise, qui était considérée comme celle de bluebird. Donc, faire ce mouvement a également nettoyé ma base de code. Je recommanderais à tout le monde de passer d'une surcharge globale de Promise . Acclamations :)

Je comprends modules > globals mais disons pour les besoins de l'argument (et/ou de la réalité) que je travaille sur un navigateur SPA de grande taille et que j'ai été chargé d'utiliser Bluebird comme polyfill Promise.

J'essaie le correctif bluebird-global.d.ts suggéré par @lhecker avec le contenu de @d-ph :

import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }

Je l'ai installé via typings qui a généré mon typings/modules/bluebird-global/index.d.ts :

// Generated by typings
// Source: src/bluebird-global.d.ts
declare module 'bluebird-global' {
// via https://github.com/DefinitelyTyped/DefinitelyTyped/issues/10801
import * as Bluebird from 'bluebird';
global { export interface Promise<T> extends Bluebird<T> {} }
}

Cependant, lorsque j'essaie de tout construire, TypeScript (v1.8.2) se plaint :

ERROR in /path/to/typings/modules/bluebird-global/index.d.ts
(6,27): error TS2665: Module augmentation cannot introduce new names in the top level scope.

ERROR in /path/to/src/bluebird-global.d.ts
(2,35): error TS2665: Module augmentation cannot introduce new names in the top level scope.

J'ai jeté un coup d'œil à l'exemple MS pour global-modifying-module.ts
https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html

Et un problème TS lié à ce message d'erreur
https://github.com/Microsoft/TypeScript/issues/6722

Mais je ne sais pas ce que je devrais faire. Quelqu'un peut-il aider?

Salut.

Pourriez-vous vérifier mon repo, qui montre le problème et la solution? lien . J'en étais assez content, jusqu'à ce que je décide d'envelopper toutes les promesses d'une tierce partie dans le constructeur Bluebird's Promise, ce que je fais maintenant. Pourriez-vous confirmer qu'après avoir suivi les étapes du fichier readme, vous ne pouvez pas compiler, mais après avoir décommenté le

// declare global {
//     export interface Promise<T> extends Bluebird<T> {}
// }

ça compile ?

Gardez à l'esprit que mon dépôt utilise TS 2 (qui est maintenant stable) et vous avez dit que vous utilisez 1.8.2. Vérifiez simplement ce qui se passe lorsque vous mettez à niveau TS vers 2.

Enfin, j'ai eu quelques problèmes pour mettre ma solution dans mon fichier global d.ts . J'ai fini par l'ajouter à chaque point d'entrée de ma compilation webpack, ce qui a résolu le problème (ce qui a du sens pour moi maintenant). Je ne connais pas votre configuration js, mais pourriez-vous essayer de mettre mon correctif dans chaque fichier qui échoue lors de la compilation (ou au moins l'un d'entre eux) et vérifier si cela aide?

Je souhaite également pouvoir "enregistrer" l'implémentation Promise Promise . J'ai lu tout ce fil mais je n'ai pas suivi une partie. La partie suggérant que les bibliothèques tierces renverront toujours l'implémentation native (par exemple non-Bluebird) Promise . Comment cela pourrait-il être, alors que ce code tiers invoque à un moment donné le constructeur Promise ( new Promise(...) ) qui a été remplacé par l'implémentation Bluebird au niveau global ?

<script src="//.../bluebird.min.js"></script>
<script>
    var promise = fetch("some url");

   promise.finally(...); 
</script>

Cela ne devrait-il pas fonctionner correctement puisque j'ai inclus Bluebird qui a remplacé l'implémentation native Promise ?

Enfin, si je fais ce remplacement global complet au moment de l'exécution, je devrais pouvoir en informer TypeScript afin qu'au moment de la compilation, toutes les promesses soient également remplacées par Bluebird.

Qu'est-ce que je rate?

Si vous utilisez la version dist de bluebird (ce que vous faites), les bibliothèques tierces utiliseront Bluebird, puisque la promesse globale est désormais Bluebird. Vous avez bien compris cette partie. Les gens mentionnent le contraire, car ils parlent de l'utilisation de Bluebird par node.js.

Tout ce fil concerne la manière pas si évidente de faire compiler ts avec cette hypothèse (que Global Promise est Bluebird). Si vous avez réussi à le faire (par exemple via le truc declare global {} ), alors vous avez terminé.

@d-ph Mais ce que je retiens, c'est que je dois le faire dans chaque fichier *.ts plutôt qu'une seule fois - est-ce exact? Peut-être qu'un résumé de la "solution" finale à ce problème serait bien. :)

Ouais, vous voyez, il n'y a pas de solution simple just copy&paste this line to your X file and everyone and their dog are happy now ici. Ou du moins je n'en suis pas conscient.

La dernière fois que je vous ai vérifié, soit :

  1. copiez et collez la ligne import * as Bluebird from 'bluebird'; declare global { export interface Promise<T> extends Bluebird<T> {} } dans chaque fichier *.ts de point d'entrée , ou
  2. enveloppez chaque promesse renvoyée par le code tiers dans la fonction constructeur de Bluebird. Au moment de l'exécution, il ne fera rien (à l'exception d'une surcharge d'exécution inutile) et au moment de la compilation, il rendra TS heureux.

Encore une fois, la solution 1. nécessite de placer ce code uniquement dans les fichiers de point d'entrée, pas dans tous les fichiers. Du moins, c'est ce qui a fonctionné pour moi (webpack + awesome-typescript-loader).

Si vous trouvez une autre solution, qui oblige littéralement les développeurs à ne mettre qu'une seule ligne dans un fichier, merci de la partager avec la communauté ;p

Merci @d-ph - pouvez-vous confirmer ce que vous entendez par "chaque point d'entrée *.ts fichier" ?

Ouais. Le fichier .ts "Point d'entrée" est le fichier .ts, que vous chargez dans votre html via la balise <script> . En d'autres termes, il s'agit du fichier à partir duquel la compilation démarre.

Après une recherche rapide sur Google tout à l'heure, j'ai trouvé ceci (ne vous embêtez pas à le lire). L'essentiel est que si vous ajoutez manuellement global { export interface Promise<T> extends Bluebird<T> {} } dans le bluebird.d.ts, vous n'avez plus besoin de le mentionner ailleurs. Je n'ai pas le temps de le tester pour le moment, mais je l'ai testé avec le référentiel test github que j'ai créé, et cela semble fonctionner.

En d'autres termes, téléchargez bluebird.d.ts et modifiez ceci :

// Generated by typings
// Source: bluebird.d.ts
declare module 'bluebird' {
// Type definitions for Bluebird v3.x.x
// Project: http://bluebirdjs.com

class Bluebird<R> implements Bluebird.Thenable<R>, Bluebird.Inspection<R> {

pour ça:

// Generated by typings
// Source: bluebird.d.ts
declare module 'bluebird' {
// Type definitions for Bluebird v3.x.x
// Project: http://bluebirdjs.com

global { export interface Promise<T> extends Bluebird<T> {} }

class Bluebird<R> implements Bluebird.Thenable<R>, Bluebird.Inspection<R> {

Évidemment, ce n'est pas idéal, surtout si vous utilisez @types/bluebird (que vous auriez besoin de supprimer maintenant, car vous utiliseriez votre bluebird.d.ts hacké personnalisé), mais bon...

J'ai donc déjà un fichier _stubs.d.ts auquel j'ai ajouté ce qui suit, et je suis beaucoup plus proche. Plus de plaintes à propos finally n'existant pas sur Promise , mais pour une raison quelconque, j'obtiens toujours des erreurs concernant delay n'existant pas sur Promise . Je n'ai pas eu à modifier bluebird.d.ts . Je vais étudier mais cela peut être une excellente solution!

declare module "bluebird-global" {
    import * as Bluebird from "bluebird";

    global { export interface Promise<T> extends Bluebird<T> { } }
}

Edit : Mon problème avec delay était dû au fait que je l'appelais de manière statique, par exemple Promise.delay(2000) .

Avec la solution que j'ai publiée ci-dessus, j'obtiens cette erreur lorsque ma fonction renvoie un Promise<T> :

erreur TS2322 : Tapez 'Bluebird' n'est pas assignable au type 'Promise'.

Je suppose que c'est parce que maintenant que j'ai remplacé Promise par Bluebird , chaque fois que j'utilise then , etc., la valeur de retour est un Bluebird<T> la place de Promise<T> .

Voici ma version finale de ce hack. Je n'aime pas faire cela, mais je l'aime mieux que les autres options. Fondamentalement, je dois réitérer les choses que j'utilise sur l'interface, en changeant le type de retour en Promise au lieu de Bluebird . Il s'agit d'un simple copier-coller du fichier de définition Bluebird autre que cela.

_stubs.d.ts

declare module "bluebird-global" {
    import * as Bluebird from "bluebird";

    global {
        export interface Promise<T> extends Bluebird<T> {
            then<U1, U2>(onFulfill: (value: T) => U1 | Bluebird.Thenable<U1>, onReject: (error: any) => U2 | Bluebird.Thenable<U2>): Promise<U1 | U2>;
            then<U>(onFulfill: (value: T) => U | Bluebird.Thenable<U>, onReject: (error: any) => U | Bluebird.Thenable<U>): Promise<U>;
            then<U>(onFulfill: (value: T) => U | Bluebird.Thenable<U>): Promise<U>;
            then(): Promise<T>;

            finally<U>(handler: () => U | Bluebird.Thenable<U>): Promise<T>;
        }
    }
}

Ce serait formidable d'avoir une définition officielle bluebird-global ou bluebird-override qui ressemble beaucoup à la définition existante mais qui utilise Promise partout au lieu de Bluebird .

Content que vous ayez pu trouver une solution.

Juste pour être complet : comme @ProTip l' a dit, en utilisant bluebird-2.0.d.ts JustWorksTM. Installez-le simplement avec npm install @types/[email protected] et ajoutez-le au compilerOptions.types de tsconfig.json :

{
    "compilerOptions": {
//     (...)
        "types": [
          "bluebird"
        ]
    },
    "include": [
        "src/**/*.ts"
    ]
}

Toute différence entre ce .d.ts et la version actuelle de Bluebird (c'est-à-dire 3.x) je recommande de pirater manuellement.

Par JustWorksTM, je veux dire : cela fonctionne pour :

a) cible es5
b) cible es6
c) cible es5 avec core-js lib

que quelqu'un utilise ou non une configuration de construction (webpack + awesome-typescript-loader). De plus, PhpStorm IDE n'est pas du tout confus.

J'ai passé un peu de temps aujourd'hui à examiner ce problème et j'ai en fait créé/mis à jour ces deux tickets dans Microsoft/TypeScript : https://github.com/Microsoft/TypeScript/issues/10178 et https://github.com/Microsoft/TypeScript /issues/12382 . Mon idée est, comme vous l'avez dit (et certains avant vous), que nous avons besoin d'un fichier bluebird-global.d.ts . Pour éviter le code dupliqué, j'ai trouvé que cela fonctionnerait:

// bluebird-global.d.ts

import * as Bluebird from "bluebird";

export as namespace Promise;
export = Bluebird;

à condition que les deux tickets susmentionnés soient résolus ou que des solutions de contournement soient trouvées. En attendant, je recommande d'utiliser le bluebird-2.0.d.ts, lors du codage pour le navigateur.

@JoshMcCullough

Comment cela pourrait-il être, alors que ce code tiers invoque à un moment donné le constructeur Promise (new Promise (...)) qui a été remplacé par l'implémentation Bliebird au niveau global ?

Plutôt facile. Essayez dans la console de votre navigateur (Chrome de préférence) :

var NativePromise = Promise;
window.Promise = function() {}; // try to overload NativePromise
NativePromise === Promise; // false. Seems it is overloaded!
// And now, let check with some native code, which return Promise, for example fetch
Object.getPrototypeOf(fetch("")) === Promise.prototype; // false, Whoops!
Object.getPrototypeOf(fetch("")) === NativePromise.prototype; // true! Double whoops!

Salut. J'ai fait ça et c'est assez fluide. Tout d'abord, ne dépendez pas de bluebird pour les projets de bibliothèque. Pour le projet d'application, importez bluebird mais pas les typages. Au point d'entrée de votre candidature, procédez comme suit :

global['Promise'] = require('bluebird')

Cela remplacera l'objet de promesse global pour l'application et toutes les bibliothèques incluses.

Salut les gars,

@types/bluebird-global est maintenant disponible. Ces typages utilisent @types/bluebird@^3.0 sous le capot et vous permettent d'utiliser les méthodes de bluebird sur le Promise global (c'est-à-dire que la compilation ts n'échoue pas).

Veuillez lire ceci pour voir comment l'utiliser.

Génial, merci @d-ph !

@d-ph Merci pour le @types/bluebird-global . Dois-je effectuer une sorte d'importation et de réaffectation dans mon projet pour utiliser bluebird en remplacement de la promesse globale ?

npm install --save-dev @types/bluebird-global puis suivez les instructions que j'ai incluses dans les saisies : lien (lien mis à jour le 2017-04-02). Cela seul devrait faire l'affaire (c'est-à-dire qu'aucune importation/réaffectation manuelle ne devrait être nécessaire).

En remarque : vous n'avez plus besoin de mentionner @types/bluebird dans votre package.json::devDependencies , car cela est implicite automatiquement.

Mise à jour du lien dans le commentaire précédent : lien

@MichaelTontchev @d-ph a-t-il une chance d'ajouter l'interface Promise.Inspection à bluebird-global ?

Salut @ksnyde.

S'il te plaît parle avec moi. Je suis le mainteneur.

Pourriez-vous confirmer que vous faites référence à cette Promise.Inspection , s'il vous plaît ?

Toutes les méthodes de cette interface sont exposées via bluebird-global . C'est-à-dire que ce qui suit compilera :

let promiseInspectionTest = new Promise<void>((resolve) => {});
promiseInspectionTest.value();

Il me semble donc que vous demandez que cela soit exposé en tant que Promise.Inspection partir de bluebird-global .

Pourriez-vous me dire si c'est un revers majeur pour vous d'utiliser ce qui suit à la place :

import * as Bluebird from "bluebird";

class Foo<T> implements Bluebird.Inspection<T> {

}

Puisque vous utilisez bluebird-global , vous pouvez également importer les typages originaux bluebird comme ça sans aucun devDependencies explicite.

Je préfère ne pas étendre davantage le Promise global sans raisons solides, car c'est un art subtil de faire fonctionner ces typages avec les typages Promise standard de lib.d.ts . Je recommande vraiment d'accéder directement à cette interface à partir des typages bluebird , car un jour les gourous du JavaScript pourraient ajouter Promise.Inspection au standard, ce qui casserait les typages bluebird-global , ce qui en conséquence causer des problèmes inutiles aux utilisateurs finaux.

De plus, même si je devais ajouter l'interface, vous auriez besoin d'attendre un temps indéterminé pour qu'elle soit fusionnée à master , car les responsables de DT sont plutôt occupés par les relations publiques ces jours-ci.

Acclamations.

J'ai en effet utilisé l'approche dont vous avez parlé pour le moment et c'est un travail adéquat. Ou peut-être que "contourner" est la mauvaise nomenclature.

Ma compréhension/perception était que l'idée derrière bluebird-global était d'exposer le sur-ensemble de fonctionnalités Promise que bluebird fournit, auquel cas en tant qu'utilisateur, je m'attendrais à ce que Bluebird.Inspection soit exposé à Promise.Inspection . Cependant, si votre intention est plutôt de simplement vous assurer que la surface officielle de l'API de Promises utilise Bluebird, je suppose que cette "solution de contournement" est en fait une solution appropriée à long terme.

Bien que je préfère mon interprétation, je vais bien utiliser la solution présentée ici si besoin est.

Bien que je voie certainement d'où vous venez avec vos attentes, la principale raison pour laquelle j'ai créé bluebird-global était de faire savoir à TypeScript que les promesses créées et renvoyées à partir de code tiers sont en fait des instances de promesses Bluebird, auxquelles il n'y avait pas d'autre alternative _pas ennuyeuse_, mais d'exposer toutes les instances et les méthodes statiques de Bluebird sur le symbole global Promise. Comme je l'ai mentionné précédemment, la façon dont cela se fait n'est pas un simple class Promise<T> extends Bluebird<T> {} (bien qu'il l'était à l'origine), mais plutôt un symbole de promesse global soigneusement corrigé. Et comme je l'ai mentionné, je préfère éviter d'avoir à entretenir quoi que ce soit, pour lequel il existe une alternative connue, qui ne vous fasse pas littéralement vous arracher les cheveux.

Désolé d'avoir dit "Non" à celui-ci. S'il y a plus de personnes qui demandent cette fonctionnalité particulière, je reconsidérerai l'ajout de celle-ci. Je ne veux pas paraître autoritaire ici - c'est un projet open-source et n'importe qui pourrait probablement pousser cette fonctionnalité. Le point que j'essaie de faire passer ici est qu'à mon avis, l'avantage d'avoir cela en fait ne dépasse pas le coût de son entretien.

Acclamations.

logique. merci pour la réflexion derrière votre approche.

@d-ph est-ce que ce qui suit a un sens pour vous ... Je reçois une erreur indiquant que "reflect" n'est pas une fonction sur le code ci-dessous:

image

Et tandis que dans l'intélisense de l'éditeur, il identifie correctement que la propriété p dans la fonction de mappage est une promesse Bluebird et a la surface API étendue uniquement trouvée sur Bluebird (versus Promise).

image

Je ne peux tout simplement pas en tirer pile ou face. J'ai remarqué que lorsque j'inspecte le _type_ de la variable d'itérateur de la carte, il s'affiche comme suit :

image

J'ai supposé que c'est finalement la raison pour laquelle j'obtiens la surface API limitée définie par bluebird-global mais je ne sais pas pourquoi elle ne se résout pas correctement.

Salut.

Je ne suis pas capable de reproduire :/ Ces extraits de code fonctionnent dans ma configuration.

D'abord. vous n'utilisez pas bluebird-global dans votre fonction allSettled() . Vous tapez directement en utilisant Bluebird . Ce n'est pas un problème, mais peut-être que vous n'étiez pas au courant. L'extrait suivant utilise bluebird-global :

function allSettled<T>(promises: Array<Promise<T>>) {
    const reflections = Promise.all<T>(promises.map((promise => {
        return promise.reflect();
    })));

    return reflections;
}

C'est-à-dire que les types d'extrait de code sont les Promise globaux (qui sont corrigés avec les méthodes de Bluebird dans bluebird-global.d.ts ). Comme je l'ai dit : celui-ci est pour votre information, au cas où vous ne le sauriez pas, car vos extraits et les miens fonctionnent de la même manière.

Revenons au problème des méthodes manquantes de Bluebird. Ma conjecture est la suivante : vous ne remplacez pas global Promise par Bluebird lors de l'exécution, puis vous exécutez allSettled() avec des promesses construites à l'aide de la promesse globale au lieu de Bluebird.

Laissez-moi vous montrer mon code et quelques captures d'écran.

function allSettled<T>(promises: Array<Promise<T>>) {
    const reflections = Promise.all<T>(promises.map((promise => {
        return promise.reflect();
    })));

    return reflections;
}

let promises = [
    Promise.resolve(),
    Promise.reject(new Error("rejected test")),
    new Promise<void>(() => {}),
];

let reflections = allSettled(promises);

console.log(reflections);
// this is part of my entry point

/*
 * Promise
 */
import * as Promise from 'bluebird';
import 'expose-loader?Promise!bluebird';

image
_Pic 1 : La promesse dans Array.map() est Bluebird (vous pouvez le dire par la présence des propriétés de soulignement comme : _bitField )_

image
_Pic 2 : Promise.reflect est bien défini dans la boucle_

Pourriez-vous définir un point d'arrêt js comme moi dans Chrome Dev Tools et voir quel est le promise à l'intérieur du .map ? Mieux encore : pourriez-vous simplement taper Promise dans la console et voir si vous obtenez le résultat suivant :
image

Si vous obtenez le résultat suivant, vous n'avez PAS remplacé global Promise par Bluebird lors de l'exécution :
image

Dans ce cas, vous devez le faire. Par exemple, comme moi dans votre fichier d'entrée.

Enfin, juste un petit retour de dev : j'utiliserais personnellement Promise<T>[] au lieu de Array<Promise<T>> . Mais cela dépend bien sûr de la préférence des développeurs. Je viens juste de l'arrière-plan C, où il n'y a pas de modèles et où les développeurs utilisent l'opérateur d'accès au tableau pour définir les types.

Acclamations.

Merci beaucoup pour une explication aussi complète. Une chose dont je n'étais pas sûr était ce que signifie le point suivant:

import 'expose-loader?Promise!bluebird' ;

@d-ph ahhh, la seule doublure ci-dessus était ce qui me manquait. Je n'y serais jamais arrivé sans votre aide ! C'est peut-être juste moi, mais je pense qu'il serait utile que le texte README fasse référence à l'utilisation de Expose Loader .

Bien que pour être juste, je ne suis toujours pas à 100% si cela nécessite alors l'utilisation de webpack ? Ma cible n'est pas le navigateur, juste une fonction de nœud.

En fait, il semble que cela ne fonctionne pas complètement car j'obtiens l'erreur suivante lorsque j'essaie d'exécuter le fichier (aucune erreur dans l'éditeur avant l'exécution):

Erreur : Impossible de trouver le module 'expose-loader?Promise!bluebird'

Vous avez raison, expose-loader est un truc de webpack (un chargeur de webpack). Donc, si vous ne laissez pas webpack traiter cette instruction import , elle ne fonctionnera pas (c'est-à-dire que vous obtiendrez l'erreur "Impossible de trouver le module").

Si vous ne pouvez pas/n'utilisez pas Webpack, vous devrez trouver un autre moyen de faire en sorte que la promesse globale soit Bluebird lors de l'exécution. Je n'ai pas beaucoup d'expérience avec le nœud, mais j'ai trouvé ceci tout à l'heure : https://github.com/petkaantonov/bluebird/issues/1026 (en remplaçant Promise dans le nœud).

Je recommande de découvrir comment utiliser async/wait dans le dernier nœud 7. Vous avez la chance de vivre à une époque où cela est disponible pour les développeurs.

Quant à la mention de l'utilisation expose-loader dans le README : j'ai déjà indiqué en haut du fichier d.ts que c'est le travail des développeurs de remplacer Promise par Bluebird dans le runtime : link . Puisqu'il y a tellement de façons de le faire, je n'en ai mentionné aucune. Gardez à l'esprit que expose-loader n'est qu'un équivalent de l'exécution de window.Promise = Bluebird . Eh bien, j'espère que Google indexera ma réponse, afin que les gens ne recherchent plus trop longtemps les options possibles ;p

@d-ph attend avec impatience l'attente asynchrone native, mais toutes ces fonctions sont sur AWS Lambda, je suis donc verrouillé sur le nœud 6.10.x pour le moment. Dans une future passe de ces fonctions, je passerai probablement de toute façon à async-wait et demanderai à Typescript de le transpiler vers ES2015, mais je ne veux pas l'introduire pour l'instant.

Quoi qu'il en soit, merci pour le lien @d-ph, je vais essayer cette approche. Oh et au cas où quelqu'un serait intéressé par la version finale de ces fonctions (qui sont très pratiques dans le monde promis):

export function allSettled<T>(promises: Array<Promise<T>>) {
  const reflections = Promise.all<Promise.Inspection<T>>( promises.map((p => p.reflect())) );
  return reflections as Promise<Array<Promise.Inspection<T>>>;
}

export function settleProps<T>(promiseHash: IDictionary<Promise<T>>) {

  const reflections: IDictionary<Promise<Promise.Inspection<T>>> = Object.keys(promiseHash)
    .reduce((newObject: IDictionary<any>, key: string) => {
      newObject[key] = promiseHash[key].reflect();
      return newObject;
    }, {} as IDictionary<Promise<Promise.Inspection<T>>>);

  return Promise.props(reflections) as Promise<IDictionary<Promise.Inspection<T>>>;
}

Des informations complètes sur Intelisync/type sont disponibles, ce qui est super sympa.

@d-ph j'espère que ça va si je relance ça avec une question pertinente...

Lorsque j'essaie d'utiliser bluebird-global, il semble que j'obtienne des différences de définition à la fois dans then et catch . Je vois que ceux-ci sont gérés spécialement dans bluebird-global, mais cela semble me limiter à l'API Promise standard au lieu d'obtenir bluebird. Par example:

Promise.resolve(true).catch(Error, () => false)

échoue car catch n'attend qu'un seul argument.

Et un autre problème :

Promise.resolve([3]).map((n: number) => true)

Échec avec :

│TS2345: Argument of type '(n: number) => boolean' is not assignable to parameter of type 'IterateFunction<{}, boolean>'.           │
│  Types of parameters 'n' and 'item' are incompatible.                                                                             │
│    Type '{}' is not assignable to type 'number'.                                                                                  │

Cela devrait-il fonctionner ou est-ce que je fais quelque chose de mal? Ils fonctionnent au moment de l'exécution, ils ne font tout simplement pas de vérification de type.

Merci!

Salut,

À propos de .catch(Error, function) , vous avez raison de dire que .then , .catch (et plus) sont gérés différemment du reste des fonctions Bluebird. Cependant, le remplacement spécifique .catch(Error, function) est inclus dans bluebird-global . J'ai revérifié et je suis capable de compiler:

Promise.resolve(true).catch(Error, () => false)

J'ai vérifié avec TS 3.0.1 et 2.9.2. Je ne sais pas pourquoi vous rencontrez un problème ici. Il y a peut-être quelque chose de spécifique dans votre projet TS qui remplace la promesse globale après bluebird-global . Je ne sais pas. Essayez peut-être de réduire la cause du problème en partant d'un projet TS très basique, puis en ajoutant plus de dépendances à partir de votre projet actuel, et voyez à quel moment il se brise.

À propos de l'autre problème : je ne sais pas pourquoi cela ne fonctionne pas. Et oui, ça devrait marcher. Veuillez créer un problème github pour cela et nous partirons de là. Voici quelques faits sur le problème :

  1. Tout ce bluebird-global fait avec .map() consiste simplement à réutiliser la définition de type bluebird.d.ts de .map() . En d'autres termes, le défaut ne devrait pas provenir des saisies bluebird-global .
  2. La ligne que vous avez mentionnée échoue sur Promise.map() , mais elle fonctionne sur Bluebird.map() :
import Bluebird = require('bluebird');

Bluebird.resolve([3]).map((n: number) => true); // works

Promise.resolve([3]).map((n: number) => true); // fails
  1. Après avoir passé du temps à déchiffrer le problème du tapuscrit (essentiellement : pourquoi TS conclut que le paramètre de n devrait être {} ), j'ai conclu que bluebird.d.ts ne devrait pas non plus fonctionner, mais cela fonctionne pour une raison inconnue pour moi. Pour faire court, ce qui suit est ce à quoi le .map() est typé après avoir supprimé toutes les couches d'abstractions :
map<U>(
    mapper: (
        item: U,
        index: number,
        arrayLength: number
    ) => U | PromiseLike<U>,
    options?: Bluebird.ConcurrencyOption
): Bluebird<T extends Iterable<any> ? U[] : never>;

Il indique que le type de retour de la fonction mapper doit être le même (ou une promesse identique) que le type de item . Dans votre exemple, le type d'élément est number et le type de retour est boolean . Je suis incapable de comprendre pourquoi cela se compile lors de l'utilisation directe Bluebird , mais ce n'est pas le cas lors de l'utilisation de la promesse globale. Au fait, cela ne fonctionne toujours pas, lorsque je change le type de retour dans votre exemple pour qu'il soit n'importe quel nombre. Cependant, cela fonctionne quand je dis que le item peut être de type any . Il y a quelque chose qui ne va pas avec bluebird.d.ts type IterableItem<R> et son utilisation dans ce contexte.

@d-ph


Éditer:

J'ai vérifié à nouveau la forme "sans couche" map() , et la fonction mapper n'est pas typée pour avoir le même type de retour que le type de item (je pensais que c'était a été). Ma faute.

Tout ce que bluebird-global fait avec .map() consiste simplement à réutiliser la définition de type .map() de bluebird.d.ts.

Le problème est-il que lorsque vous copiez le type hors de la classe générique Bluebird<R> , il est par défaut de {} pour R puisqu'il ne peut pas le déduire du parent ?

Je me demande si map: typeof Bluebird<T>.prototype.map fonctionnerait ? (je n'ai pas encore essayé)

IMPORTANT:
Si vous utilisez @types/bluebird-global , supprimez vos dépendances @types/bluebird comme indiqué par @d-ph

npm install --save-dev @types/bluebird-global puis suivez les instructions que j'ai incluses dans les saisies : lien (lien mis à jour le 2017-04-02). Cela seul devrait faire l'affaire (c'est-à-dire qu'aucune importation/réaffectation manuelle ne devrait être nécessaire).

En remarque : vous n'avez plus besoin de mentionner @types/bluebird dans votre package.json::devDependencies , car cela est implicite automatiquement.

Avoir les deux provoquait une correspondance manquée entre les types renvoyés par @types/bluebird et ma promesse globale ( @types/bluebird-global )

Cette page vous a été utile?
0 / 5 - 0 notes