Definitelytyped: bluebird 3.0: como usá-lo como sobrecarga para promessa global?

Criado em 24 ago. 2016  ·  44Comentários  ·  Fonte: DefinitelyTyped/DefinitelyTyped

Ei a todos!

Estou usando o bluebird como substituto do objeto Promise global. Tentei atualizar typedefs para a versão mais recente, publicada por @lhecker , e encontrei o problema: global Promsie não está sobrecarregado agora por padrão.
Como posso alcançar o comportamento anterior? Talvez pudéssemos ter bluebird-global.d.ts, por exemplo?

Comentários muito úteis

Oi pessoal,

@types/bluebird-global já está disponível. Esses tipos usam @types/bluebird@^3.0 sob o capô e permitem que você use os métodos do bluebird no Promise global (ou seja, a compilação ts não falha).

Por favor, leia isto para ver como usá-lo.

Todos 44 comentários

Bem, IMHO as definições anteriores do Bluebird também não eram uma boa solução, porque elas vazavam muito no namespace global e acho que foi uma boa ideia reduzir esforços redundantes.

A maneira como as definições anteriores funcionaram é definindo declare var Promise: PromiseConstructor; enquanto PromiseConstructor era a interface Bluebird anterior (definida globalmente).

Ou seja, se você criar um arquivo local *.d.ts e adicionar algo assim, talvez funcione?

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

pode funcionar talvez?

Infelizmente não. Porque eu tenho muito código, que está escrito assim:

declare function doLoadData(): Promise<Data>

como você pode ver, a função retorna Promise<T> , que é o padrão Promsie , não o bluebird. declarando var Promise: Bluebird<any> vou sobrecarregar o construtor Promise padrão, não a interface.

Voltei para as digitações 2.0 por esse motivo.

@Strate Ah caramba, escrevi um longo comentário sobre o que penso sobre isso e o que devemos tentar fazer. Mas parece que esqueci de enviar e finalmente foi perdido...

Muitas pessoas parecem ter esse problema.

Eu criei um repositório que mostra o problema: https://github.com/d-ph/typescript-bluebird-as-global-promise

git clone && npm install && npm run tsc

O problema:
Os arquivos d.ts terceiros são digitados em Promise . Este Promise é definido pelo lib.es6.d.ts do typescript (quando "target": "es6" ) ou por outras libs, por exemplo core-js (muito popular, ao compilar para es5 com typescript) . O bluebird.d.ts mais recente não é declarado como Promise global, embora o bluebird.js se exponha como o Promise global.

Resultado:
Os desenvolvedores não podem usar a funcionalidade do bluebird nas Promises retornadas do código de terceiros (a compilação falha).

Resultado esperado:
Os desenvolvedores podem usar a funcionalidade do bluebird nas Promises retornadas do código de terceiros (compilação bem-sucedida).

Não tenho certeza de quem é o mantenedor dos tipos de bluebird. @lhecker , você é o azarado a ser devolvido por git blame . Você poderia nos dizer, qual é o motivo preferido de usar tipagens bluebird de tal forma, que o projeto github, eu linkei acima, compile?

Percursos atuais:

  1. O mais sujo. Nunca importe bluebird e nunca use tipagens de bluebird. Para funções bluebird Promise use isto: Promise["config"]({}); Promise.resolve("foo")["finally"](() => { console.log("lol"); }) para silenciar o compilador. Ou seja, use o operador de acesso ao array: [""]
  2. Sujo e irritante. Em cada arquivo ts de entrada em seu aplicativo, adicione estas duas linhas:
import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }

Para funções estáticas do Bluebird, use Bluebird.config({}) em vez de Promise.config({}) .

A desvantagem é que os IDEs lutam para interpretar esse hack corretamente.

Hmm... Foi meio imprevisto para mim que vocês tenham problemas com isso, já que todos vocês estão usando o Bluebird de uma forma que eu e muitos outros não. Na verdade, nem eu mesmo escrevi as datilografias! Eu simplesmente copiei os únicos existentes para 3.0 daqui , porque ter qualquer tipo para 3.0 é melhor do que não ter nenhum, certo?

A questão é que a direção atual do TypeScript é claramente modules > globals que é algo que eu realmente aprovo. Mas isso também significa que os módulos nunca devem modificar objetos globais, especialmente se você considerar que Bluebird não substitui o Promise global em todos os casos! Ou para colocar assim:

O que acontece com sua "segurança de tipo" se você chamar Promise.noConflict() literalmente em qualquer lugar do seu código? Ele reverterá o tipo global Promise de volta ao original e fará com que seu código falhe, mesmo que tsc tenha dito que está tudo bem.

Então sim... @d-ph. Seu segundo passo a passo é o que você deveria ter considerado fazer o tempo todo, já que está no espírito dos sistemas de módulos. Mas sei que esta é apenas a solução ideal para bibliotecas, embora possa ser realmente irritante para aplicativos. Concordo que os sistemas de aplicativos devem definitivamente ser capazes de substituir o objeto global Promise e também ter as tipagens correspondentes para esse caso de uso, pois estava disponível na versão 2.0.

No final, acho que, à luz da ideologia do TypeScript, estender o tipo global Promise deve ser feito _muito_ com cuidado (lembre-se do problema noConflict() etc.) e, se for o caso, apenas como opt-in.

IMO o caminho a seguir é escrever um arquivo bluebird-global.d.ts (ou similar) de algum tipo que estenda o objeto global Promise com as mesmas declarações de interface encontradas no arquivo bluebird.d.ts . E se você precisar usá-los, deverá importá-los explicitamente em vez de tê-los sempre incluídos. Dessa forma, você pode ter digitações seguras _e_ corretas para a maioria dos casos de uso e especialmente ao escrever bibliotecas, tendo acesso aos benefícios adicionais de sobrescrever o Promise global em aplicativos.

Se você acha que essa ideia é boa e se você tem algum tempo livre, seria legal se você pudesse criar um PR. Tenho certeza que muitos ficariam muito felizes com tal contribuição. 🙂

Estou formulando assim porque não estou em condições de escrever essas digitações, por falta de tempo e não precisar de tais digitações no momento. Espero que você possa entender isso.

@lhecker acho que posso concordar com você. Porque se tivermos uma substituição global da Promise para a do bluebird, apenas hackeamos o compilador datilografado, mas não o mundo real. Por exemplo, com o typescript Promise substituído, pensará que fetch retorna o do bluebird:

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

Sem envolver fetch no Promise.resolve do bluebird, você não obterá, por exemplo, o método .finally em result :

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

Então, acho que importar explicitamente bluebird em cada uso é a melhor solução:

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

Vou refatorar meu código.

Obrigado pela resposta, @lhecker .

Eu concordo com tudo o que você disse. E eu gosto da solução da @Strate de envolver o código de terceiros com o método Promise.resolve() , para transformar a promessa es6 em Bluebird (ou Bluebird em Bluebird, porque quero manter a promessa global do Bluebird no tempo de execução, então não não precisa confiar no código de terceiros para lidar com seus erros corretamente, mas isso não vem ao caso).

Parece que eu não sabia como fazer isso corretamente. O que eu acho que outros se beneficiariam é de mais documentação sobre como lidar com esse problema, porque todos que vêm do mundo da programação do navegador (em oposição a: de nodejs/typescript) o acessam depois de:

  1. npm install <absolutely everything that uses es6 promise>
  2. npm install bluebird @types/bluebird
  3. use o código de terceiros com texto datilografado

Eu também me beneficiaria de ter esta seção "Como usar no caso de código de terceiros ser digitado em relação à promessa es6" documentada em algum lugar para referência futura. Ou seja, ter o

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

e

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

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

em um leia-me ou no bloco doc na parte superior do arquivo bluebird.d.ts.

O que você acha?

Concluí a mudança de substituição global de Promise para bluebird e encontrei alguns problemas, em que bibliotecas de terceiros retornam ES6 Promise, que foi tratada como a do bluebird. Então, fazer esse movimento limpou minha base de código também. Eu recomendaria a todos que saíssem da sobrecarga global de Promise . Felicidades :)

Eu entendo modules > globals mas digamos para fins de argumento (e/ou realidade) que estou trabalhando em um SPA de navegador grande e fui encarregado de usar o Bluebird como nosso polyfill Promise.

Estou tentando a correção bluebird-global.d.ts sugerida por @lhecker com o conteúdo de @d-ph:

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

Eu instalei via typings que gerou meu 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> {} }
}

No entanto, quando tento construir tudo, o TypeScript (v1.8.2) reclama:

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.

Eu dei uma olhada no exemplo do MS para global-modifying-module.ts
https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html

E um problema de TS relacionado a esta mensagem de erro
https://github.com/Microsoft/TypeScript/issues/6722

Mas estou sem saber o que deveria estar fazendo. Alguém pode ajudar?

Oi.

Você poderia verificar meu repositório, que mostra o problema e a solução? ligação . Fiquei muito feliz com isso, até que decidi envolver todas as promessas de terceiros no construtor Promise do bluebird, que é o que faço agora. Você poderia confirmar que depois de seguir as etapas no leia-me, você não pode compilar, mas depois de descomentar o

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

ele compila?

Tenha em mente que meu repositório usa TS 2 (que agora está estável) e você disse que está usando 1.8.2. Basta verificar o que acontece, quando você atualiza o TS para 2.

Finalmente, tive alguns problemas em colocar minha solução no meu arquivo global d.ts . Acabei adicionando-o a todos os pontos de entrada da minha compilação do webpack, o que resolveu o problema (o que faz sentido para mim agora). Não conheço a configuração do seu js, mas você poderia tentar colocar minha correção em todos os arquivos que falham durante a compilação (ou pelo menos um deles) e verificar se isso ajuda?

Eu também quero ser capaz de "registrar" a implementação Promise do Bluebird como o Promise global. Li todo o tópico, mas não sigo uma parte. A parte sugerindo que bibliotecas de terceiros ainda retornarão a implementação nativa (por exemplo, não Bluebird) Promise . Como poderia ser isso, quando esse código de terceiros está em algum momento invocando o construtor Promise ( new Promise(...) ) que foi substituído pela implementação do Bluebird em nível global?

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

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

Isso não deveria funcionar bem, já que incluí o Bluebird, que substituiu a implementação nativa Promise ?

Por fim, se eu estiver fazendo essa substituição global completa em tempo de execução, devo poder informar o TypeScript sobre isso para que, em tempo de compilação, todas as promessas também sejam substituídas pelo Bluebird.

o que estou perdendo?

Se você usar a versão dist do bluebird (o que você faz), as bibliotecas de terceiros usarão o Bluebird, já que a promessa global agora é o Bluebird. Você acertou essa parte. As pessoas mencionam o contrário, porque falam sobre o uso node.js do Bluebird.

Este tópico inteiro é sobre a maneira não tão óbvia de fazer ts compilar com essa suposição (que a promessa global é o Bluebird). Se você conseguiu fazer isso (por exemplo, através da coisa declare global {} ), então está feito.

@d-ph Mas minha conclusão é que eu tenho que fazer isso em todos os arquivos *.ts em vez de apenas uma vez - isso está correto? Talvez um resumo da "solução" final para este problema fosse bom. :)

Sim, você vê, não há nenhum tipo de solução simples just copy&paste this line to your X file and everyone and their dog are happy now aqui. Ou pelo menos não estou ciente disso.

A última vez que verifiquei você:

  1. copie e cole a linha import * as Bluebird from 'bluebird'; declare global { export interface Promise<T> extends Bluebird<T> {} } em cada arquivo *.ts de ponto de entrada , ou
  2. envolva todas as promessas retornadas do código de terceiros na função construtora do Bluebird. Em tempo de execução não fará nada (exceto sobrecarga desnecessária de tempo de execução) e em tempo de compilação fará TS feliz.

Novamente, a solução 1. requer colocar esse código apenas em arquivos de ponto de entrada, não em todos os arquivos. Pelo menos, foi isso que funcionou para mim (webpack + awesome-typescript-loader).

Se você encontrar outra solução, que literalmente exija que os desenvolvedores coloquem apenas 1 linha em 1 arquivo, compartilhe com a comunidade ;p

Obrigado @d-ph -- você pode confirmar o que quer dizer com "todos os arquivos *.ts de ponto de entrada"?

Sim. O arquivo .ts "Ponto de entrada" é o arquivo .ts, que você carrega em seu html via tag <script> . Em outras palavras, este é o arquivo a partir do qual a compilação começa.

Depois de uma pesquisa rápida agora, encontrei isso (não se incomode em ler). A conclusão é que, se você adicionar manualmente global { export interface Promise<T> extends Bluebird<T> {} } no bluebird.d.ts, não precisará mencionar isso novamente em nenhum outro lugar. Não tenho tempo para testá-lo agora, mas testei com o repositório test github que criei e parece funcionar.

Em outras palavras, baixe bluebird.d.ts e altere isso:

// 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> {

para isso:

// 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> {

Obviamente, isso não é o ideal, especialmente se você usar @types/bluebird (que você precisaria remover agora, porque você usaria seu bluebird.d.ts hackeado personalizado), mas bem...

Então eu já tenho um arquivo _stubs.d.ts ao qual adicionei o seguinte, e estou muito mais próximo. Não há mais reclamações sobre finally não existir em Promise , mas por algum motivo, ainda recebo erros sobre delay não existir em Promise . Não precisei editar bluebird.d.ts . Irá investigar, mas esta pode ser uma ótima solução!

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

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

Edit: Meu problema com delay foi porque eu estava chamando estaticamente, por exemplo, Promise.delay(2000) .

Com a solução que postei acima, estou recebendo este erro quando minha função retorna um Promise<T> :

erro TS2322: Digite 'Bluebird' não é atribuível ao tipo 'Promise'.

Acho que é porque agora que substituí Promise por Bluebird , sempre que uso then , etc., o valor de retorno é Bluebird<T> de Promise<T> .

Aqui está a minha versão final deste hack. Eu não gosto de fazer isso, mas eu gosto mais do que as outras opções. Basicamente, tenho que reiterar as coisas que estou usando na interface, alterando o tipo de retorno para Promise em vez de Bluebird . Esta é uma cópia e colagem direta do arquivo de definição do Bluebird diferente disso.

_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>;
        }
    }
}

Seria ótimo ter uma definição oficial bluebird-global ou bluebird-override que se parece muito com a definição existente, mas usa Promise em todos os lugares em vez de Bluebird .

Que bom que você conseguiu encontrar uma solução.

Apenas para completar: como disse @ProTip , usando bluebird-2.0.d.ts JustWorksTM. Basta instalá-lo com npm install @types/[email protected] e adicioná-lo ao compilerOptions.types do tsconfig.json:

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

Quaisquer diferenças entre esse .d.ts e a versão atual do Bluebird (ou seja, 3.x) recomendo hackear manualmente.

Por JustWorksTM quero dizer: funciona para:

a) alvo es5
b) alvo es6
c) destino es5 com lib core-js

independentemente de alguém usar uma configuração de compilação (webpack + awesome-typescript-loader) ou não. Além disso, o PhpStorm IDE não é confuso.

Passei algum tempo hoje analisando esse problema e, na verdade, criei/atualizei esses dois tickets no Microsoft/TypeScript: https://github.com/Microsoft/TypeScript/issues/10178 e https://github.com/Microsoft/TypeScript /questões/12382 . Minha ideia é, como você disse (e alguns antes de você), que precisamos de um arquivo bluebird-global.d.ts . Para evitar código duplicado, descobri que isso funcionaria:

// bluebird-global.d.ts

import * as Bluebird from "bluebird";

export as namespace Promise;
export = Bluebird;

desde que os dois tickets mencionados sejam resolvidos ou soluções alternativas sejam encontradas. Enquanto isso, recomendo usar o bluebird-2.0.d.ts, ao codificar para o navegador.

@JoshMcCullough

Como poderia ser isso, quando esse código de terceiros está em algum momento invocando o construtor Promise (new Promise(...)) que foi substituído pela implementação do Bliebird em nível global?

Bem fácil. Tente no console do seu navegador (preferencialmente Chrome):

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!

Oi. Eu tenho feito isso e é bem tranquilo. Em primeiro lugar, não dependa do bluebird para projetos de biblioteca. Para o projeto do aplicativo, importe bluebird, mas não as tipagens. No ponto de entrada do seu aplicativo, faça o seguinte:

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

Isso substituirá o objeto de promessa global para o aplicativo e todas as bibliotecas incluídas.

Oi pessoal,

@types/bluebird-global já está disponível. Esses tipos usam @types/bluebird@^3.0 sob o capô e permitem que você use os métodos do bluebird no Promise global (ou seja, a compilação ts não falha).

Por favor, leia isto para ver como usá-lo.

Incrível, obrigado @d-ph!

@d-ph Obrigado pelo @types/bluebird-global . Preciso fazer algum tipo de importação e reatribuição no meu projeto para usar o bluebird como substituto do Promise global?

npm install --save-dev @types/bluebird-global e depois siga as instruções que incluí na digitação: link (link atualizado 2017-04-02). Isso por si só deve fazer o truque (ou seja, nenhuma importação/reatribuição manual deve ser necessária).

Como uma nota lateral: você não precisa mais mencionar @types/bluebird em seu package.json::devDependencies , porque isso está implícito automaticamente.

Atualize para o link no comentário anterior: link

@MichaelTontchev @d-ph alguma chance de termos a interface Promise.Inspection adicionada ao bluebird-global ?

Olá @ksnyde.

Por Favor, fale comigo. Eu sou o mantenedor.

Você poderia confirmar que está se referindo a este Promise.Inspection , por favor?

Todos os métodos dessa interface são expostos via bluebird-global . Ou seja, o seguinte irá compilar:

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

Então, parece-me que você está solicitando que isso seja exposto como Promise.Inspection de bluebird-global .

Você poderia me dizer se este é um grande revés para você usar o seguinte:

import * as Bluebird from "bluebird";

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

}

Como você está usando bluebird-global , você também pode importar os tipos bluebird originais assim sem nenhum devDependencies explícito.

Prefiro não estender mais o Promise global sem razões sólidas por trás dele, porque é uma arte sutil fazer com que essas tipagens funcionem com as tipagens Promise padrão de lib.d.ts . Eu realmente recomendo acessar essa interface diretamente das digitações bluebird , porque um dia os gurus do JavaScript podem adicionar Promise.Inspection ao padrão, o que quebraria as digitações bluebird-global , o que em consequência causar problemas desnecessários aos usuários finais.

Além disso, mesmo que eu adicionasse a interface, você precisaria esperar um tempo não especificado para que ela fosse mesclada para master , porque os mantenedores da DT estão meio ocupados com PRs hoje em dia.

Felicidades.

De fato, tenho usado a abordagem que você discutiu por enquanto e é uma solução adequada. Ou talvez "contornar" seja a nomenclatura errada.

Meu entendimento/percepção foi que a ideia por trás do bluebird-global era expor o superconjunto de funcionalidades do Promise que o bluebird fornece, caso em que, como usuário, eu _esperaria_ que Bluebird.Inspection fosse exposto em Promise.Inspection . No entanto, se sua intenção é apenas garantir que a superfície oficial da API do Promises use o Bluebird, acho que essa "contornação" é realmente uma solução apropriada de longo prazo.

Embora eu prefira minha interpretação, ficarei bem usando a solução apresentada aqui, se necessário.

Embora eu certamente veja de onde você está vindo com suas expectativas, a principal razão pela qual criei bluebird-global foi para informar o TypeScript, que as promessas criadas e retornadas de código de terceiros são, na verdade, instâncias de promessas do Bluebird, para as quais não havia outra alternativa _não irritante_ a não ser expor todas as instâncias e métodos estáticos do Bluebird no símbolo global de Promise. Como mencionei anteriormente, a maneira como isso é feito não é um simples class Promise<T> extends Bluebird<T> {} (embora fosse originalmente), mas sim um símbolo global de promessa cuidadosamente corrigido. E, como mencionei, prefiro evitar ter que manter qualquer coisa, para a qual existe uma alternativa conhecida, que não faça você literalmente arrancar os cabelos da cabeça.

Desculpe por dizer "não" para este. Se houver mais pessoas solicitando esse recurso específico, reconsiderarei adicioná-lo. Eu não quero parecer autoritário aqui -- este é um projeto de código aberto e qualquer um provavelmente poderia empurrar esse recurso. O ponto que estou tentando passar aqui é que, na minha opinião, o benefício de ter isso não superam o custo de manutenção.

Felicidades.

faz sentido. obrigado pelo pensamento por trás de sua abordagem.

@d-ph faz o seguinte sentido para você ... Estou recebendo um erro informando que "reflect" não é uma função no código abaixo:

image

E enquanto no intelisense do editor está identificando corretamente que a propriedade p na função de mapeamento é uma promessa do Bluebird e tem a superfície de API estendida encontrada apenas no Bluebird (versus Promise).

image

Eu simplesmente não posso fazer cara ou coroa com isso. Percebi que quando inspeciono o _type_ da variável iteradora do mapa, ele aparece como:

image

Suponho que seja por isso que estou obtendo a superfície limitada da API definida por bluebird-global mas não sei por que não está resolvendo corretamente.

Oi.

Não consigo reproduzir :/ Esses trechos de código funcionam na minha configuração.

Em primeiro lugar. você não está usando bluebird-global em sua função allSettled() . Você digita diretamente usando Bluebird . Isso não é um problema, mas talvez você não estivesse ciente disso. O snippet a seguir usa bluebird-global :

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

    return reflections;
}

Ou seja, esse snippet digita para o Promise global (que é corrigido com os métodos do Bluebird em bluebird-global.d.ts ). Como eu disse: este é para sua informação, caso você não esteja ciente disso, porque o seu e o meu snippets funcionam da mesma forma.

De volta ao problema da falta dos métodos do Bluebird. Meu palpite é: você não substitui Promise global por Bluebird em tempo de execução e então executa allSettled() com Promises construídas usando o Promise global em vez de Bluebird.

Deixe-me mostrar meu código e algumas capturas de tela.

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: A promessa no Array.map() é Bluebird (você pode dizer pela presença das propriedades de sublinhado como: _bitField )_

image
_Pic 2: Promise.reflect está realmente definido no loop_

Você poderia definir um ponto de interrupção js como eu no Chrome Dev Tools e ver qual é o promise dentro do .map ? Melhor ainda: você poderia simplesmente digitar Promise no console e ver se obtém a seguinte saída:
image

Se você obtiver a seguinte saída, NÃO substituiu Promise global por Bluebird em tempo de execução:
image

Nesse caso você precisa fazer isso. Por exemplo, como eu em seu arquivo de entrada.

Por fim, apenas um feedback do desenvolvedor: eu pessoalmente usaria Promise<T>[] em vez de Array<Promise<T>> . Mas isso depende da preferência dos desenvolvedores, é claro. Acabei de vir do fundo C, onde não há modelagem e onde os desenvolvedores usam o operador de acesso ao array para definir tipos.

Felicidades.

Muito obrigado por uma explicação tão completa. Uma coisa que eu não tinha certeza era o que o seguinte aponta para:

import 'expose-loader?Promise!bluebird';

@d-ph ahhh, o forro acima era o que estava faltando. Nunca teria chegado lá sem a sua ajuda! Pode ser apenas eu, mas acho que seria útil se o texto README tivesse uma referência ao uso de Expose Loader .

Embora para ser justo, ainda não estou 100% se isso requer o uso de webpack? Meu alvo não é o navegador, apenas uma função de nó.

Na verdade, parece que não está funcionando totalmente, pois recebo o seguinte erro quando tento executar o arquivo (sem erros no editor antes do tempo de execução):

Erro: Não é possível encontrar o módulo 'expose-loader?Promise!bluebird'

Você está certo, expose-loader é uma coisa de webpack (um carregador de webpack). Portanto, se você não permitir que o webpack processe essa instrução import , ela não será executada (ou seja, você receberá o erro "Não é possível encontrar o módulo").

Se você não puder/não usar o webpack, você precisará encontrar outra maneira de tornar a Promise global Bluebird em tempo de execução. Não tenho muita experiência com node, mas encontrei isso agora: https://github.com/petkaantonov/bluebird/issues/1026 (substituindo Promise no node).

Eu recomendo descobrir como usar async/await no último nó 7. Você tem sorte o suficiente para viver em tempos, quando isso está disponível para devs.

Quanto a mencionar o uso de expose-loader no README: Eu já afirmo no topo do arquivo d.ts , que este é o trabalho dos devs para substituir Promise por Bluebird em tempo de execução: link . Como existem tantas maneiras de fazer isso, eu não mencionei nenhuma delas. Tenha em mente que expose-loader é apenas o equivalente a executar window.Promise = Bluebird . Bem, espero que o google indexe minha resposta, para que as pessoas não procurem mais opções possíveis por muito tempo ;p

@d-ph ansioso pelo async-await nativo, mas todas essas funções estão no AWS Lambda, então estou bloqueado no nó 6.10.x por enquanto. Em uma passagem futura dessas funções, provavelmente mudarei para async-await de qualquer maneira e farei com que o Typescript o transpile para o ES2015, mas não quero introduzir isso ainda.

De qualquer forma, obrigado pelo link @d-ph, vou tentar essa abordagem. Ah, e caso alguém esteja interessado na versão final dessas funções (que são bastante úteis na terra das promessas):

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>>>;
}

Informações completas de intelisync/tipo estão disponíveis, o que é super legal.

@d-ph espero que esteja tudo bem se eu reviver isso com uma pergunta relevante ...

Quando tento usar o bluebird-global, pareço estar recebendo diferenças por definição em then e catch . Eu vejo que eles são tratados especialmente no bluebird-global, mas parece me limitar à API Promise padrão em vez de obter o bluebird. Por exemplo:

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

falha porque catch espera apenas 1 argumento.

E outra questão:

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

Falha com:

│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'.                                                                                  │

Eles devem funcionar ou estou fazendo algo errado? Eles funcionam em tempo de execução, eles simplesmente não verificam a digitação.

Obrigado!

Oi,

Sobre o .catch(Error, function) , você está correto ao dizer que .then , .catch (e mais) são tratados de forma diferente do resto das funções do Bluebird. No entanto, a substituição específica .catch(Error, function) está incluída em bluebird-global . Eu verifiquei duas vezes e sou capaz de compilar:

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

Eu verifiquei com TS 3.0.1 e 2.9.2. Eu não sei porque você pode estar tendo um problema aqui. Talvez haja algo específico em seu projeto TS que substitua a promessa global após bluebird-global . Eu não sei. Talvez tente diminuir o que está causando o problema começando de um projeto TS muito básico e adicionando mais dependências do seu projeto atual e veja em que ponto ele quebra.

Sobre o outro problema: não sei porque não funciona. E sim, deve funcionar. Por favor, crie um problema no github para ele e vamos a partir daí. Aqui estão alguns fatos sobre o problema:

  1. Tudo que bluebird-global faz com .map() é apenas reutilizar a definição de tipo .map() bluebird.d.ts Em outras palavras, o defeito não deve ser proveniente das digitações bluebird-global .
  2. A linha que você mencionou falha em Promise.map() , mas funciona em Bluebird.map() :
import Bluebird = require('bluebird');

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

Promise.resolve([3]).map((n: number) => true); // fails
  1. Depois de gastar algum tempo para descriptografar o problema do texto datilografado (essencialmente: por que TS conclui que o parâmetro de n deveria ser {} ), concluí que bluebird.d.ts também não deveria funcionar, mas funciona por motivo desconhecido para mim. Para encurtar a história, o seguinte é o que o .map() é digitado depois de remover todas as camadas de abstrações:
map<U>(
    mapper: (
        item: U,
        index: number,
        arrayLength: number
    ) => U | PromiseLike<U>,
    options?: Bluebird.ConcurrencyOption
): Bluebird<T extends Iterable<any> ? U[] : never>;

Ele diz que o tipo de retorno da função mapper deve ser o mesmo (ou uma promessa do mesmo) que o tipo de item . No seu exemplo, o tipo de item é number e o tipo de retorno é boolean . Não consigo compreender por que isso compila ao usar Bluebird diretamente, mas não ao usar a promessa global. A propósito, ainda não funciona, quando altero o tipo de retorno no seu exemplo para qualquer número. No entanto, funciona quando digo que o item pode ser do tipo any . Há algo errado com bluebird.d.ts de type IterableItem<R> e seu uso neste contexto.

@d-ph


Editar:

Eu verifiquei o formulário "unlayered" map() novamente, e a função mapper não é digitada para ter o mesmo tipo de retorno que o tipo de item (eu pensei que foi). Foi mal.

Tudo que o bluebird-global faz com .map() é apenas reutilizar a definição de tipo .map() do bluebird.d.ts.

O problema é que, quando você copia o tipo da classe genérica Bluebird<R> , o padrão é {} para R , pois não pode inferir do pai?

Gostaria de saber se map: typeof Bluebird<T>.prototype.map funcionaria? (ainda não testei isso)

IMPORTANTE:
Se estiver usando @types/bluebird-global , exclua suas dependências @types/bluebird como dito por @d-ph

npm install --save-dev @types/bluebird-global e siga as instruções que incluí na digitação: link (link atualizado em 2017-04-02). Isso por si só deve fazer o truque (ou seja, nenhuma importação/reatribuição manual deve ser necessária).

Como uma nota lateral: você não precisa mais mencionar @types/bluebird em seu package.json::devDependencies , porque isso está implícito automaticamente.

Ter ambos estava causando uma falta de correspondência entre os tipos retornados por @types/bluebird e minha promessa global ( @types/bluebird-global )

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

victor-guoyu picture victor-guoyu  ·  3Comentários

tyv picture tyv  ·  3Comentários

JudeAlquiza picture JudeAlquiza  ·  3Comentários

ArtemZag picture ArtemZag  ·  3Comentários

Zzzen picture Zzzen  ·  3Comentários