Definitelytyped: bluebird 3.0: как использовать его в качестве перегрузки для глобального обещания?

Созданный на 24 авг. 2016  ·  44Комментарии  ·  Источник: DefinitelyTyped/DefinitelyTyped

Всем привет!

Я использую bluebird в качестве замены глобального объекта Promise. Я пытался обновить typedefs до последней версии, опубликованной @lhecker , и столкнулся с проблемой: global Promsie теперь не перегружается по умолчанию.
Как мне добиться прежнего поведения? Может быть, например, у нас есть bluebird-global.d.ts?

Самый полезный комментарий

Привет, ребята,

@types/bluebird-global теперь доступен. Эти типизации используют @types/bluebird@^3.0 под капотом и позволяют вам использовать методы bluebird в глобальном Promise (т.е. компиляция ts не завершается ошибкой).

Пожалуйста, прочтите это , чтобы узнать, как его использовать.

Все 44 Комментарий

Ну, ИМХО, предыдущие определения Bluebird тоже не были хорошим решением, потому что они сильно просачивались в глобальное пространство имен, и я думаю, что было хорошей идеей уменьшить избыточные усилия.

Однако предыдущие определения работали, определяя declare var Promise: PromiseConstructor; , тогда как PromiseConstructor был предыдущим (глобально определенным) интерфейсом Bluebird.

Т.е. если вы создадите локальный файл *.d.ts и добавите что-то вроде этого, возможно, это сработает?

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

может сработает может?

к сожалению нет. Потому что у меня много кода, который написан так:

declare function doLoadData(): Promise<Data>

как видите, функция возвращает Promise<T> , что является стандартным Promsie , а не bluebird. объявив var Promise: Bluebird<any> , я перегружу стандартный конструктор Promise, а не интерфейс.

По этой причине я вернулся к типизации 2.0.

@Strate Ах, черт возьми, я написал длинный комментарий о том, что я думаю об этом и что мы должны попытаться сделать. Но, кажется, я забыл отправить его, и он был окончательно потерян...

Кажется, у многих людей есть эта проблема.

Я создал репо, в котором показана проблема: https://github.com/d-ph/typescript-bluebird-as-global-promise .

git clone && npm install && npm run tsc

Проблема:
Сторонние файлы d.ts набираются вместо Promise . Этот Promise определяется либо lib.es6.d.ts машинописного текста (когда "target": "es6" ), либо другими библиотеками, например, core-js (очень популярен при компиляции в es5 с помощью машинописного текста) . Последний bluebird.d.ts не объявлен как глобальный Promise , хотя bluebird.js выставляет себя как глобальный Promise .

Результат:
Разработчики не могут использовать функциональность bluebird в обещаниях, возвращаемых из стороннего кода (сбой компиляции).

Ожидаемый результат:
Разработчики могут использовать функциональность bluebird для промисов, возвращенных из стороннего кода (компиляция прошла успешно).

Я не уверен, кто поддерживает типизацию bluebird. @lhecker , ты неудачник, которого вернули git blame . Не могли бы вы рассказать нам, почему предпочтительнее использовать типизацию bluebird таким образом, чтобы проект github, на который я ссылался выше, компилировался?

Текущие обходы:

  1. Самый грязный. Никогда не импортируйте bluebird и никогда не используйте типизацию bluebird. Для функций bluebird Promise используйте это: Promise["config"]({}); Promise.resolve("foo")["finally"](() => { console.log("lol"); }) для отключения компилятора. Т.е. использовать оператор доступа к массиву: [""]
  2. Грязный и раздражающий. В каждый файл ts записи в вашем приложении добавьте эти две строки:
import * as Bluebird from 'bluebird';
declare global { export interface Promise<T> extends Bluebird<T> {} }

Для статических функций Bluebird используйте Bluebird.config({}) вместо Promise.config({}) .

Недостатком является то, что IDE не могут правильно интерпретировать этот хак.

Хм... Для меня было непредвиденным, что у вас, ребята, возникнут проблемы с этим, поскольку вы все используете Bluebird так, как не использую я и многие другие. На самом деле я даже не писал наборы сам! Я просто скопировал отсюда единственные существующие для 3.0, потому что иметь какие-либо типизации для 3.0 лучше, чем не иметь ничего, верно?

Дело в том, что текущее направление, в котором движется TypeScript, явно modules > globals , что я действительно одобряю. Но это также означает, что модули никогда не должны изменять глобальные объекты, особенно если учесть, что Bluebird не заменяет глобальный Promise в каждом случае! Или, говоря так:

Что произойдет с вашей «безопасностью типов», если вы вызовете Promise.noConflict() буквально в любом месте вашего кода? Он вернет глобальный тип Promise обратно к исходному и приведет к сбою кода, даже если tsc сказал, что все в порядке.

Так что да... @d-ph. Второй обходной путь — это то, что вы должны были делать все это время, поскольку он соответствует духу модульных систем. Но я знаю, что это идеальное решение только для библиотек, тогда как для приложений оно _может_ сильно раздражать. Я согласен с тем, что прикладные системы должны, по крайней мере, иметь возможность заменить глобальный объект Promise , а также иметь соответствующие типизации для этого варианта использования, как это было доступно в версии 2.0.

В конце концов, я думаю, что в свете идеологии TypeScript расширение глобального типа Promise должно быть сделано _очень_ осторожно (вспомните проблему с noConflict() и т. д.) и если так, то только как подписка.

IMO путь вперед состоит в том, чтобы написать bluebird-global.d.ts (или аналогичный) файл некоторого вида, который расширяет глобальный объект Promise с теми же объявлениями интерфейса, что и в файле bluebird.d.ts . И если вам нужно их использовать, вы должны явно импортировать их вместо того, чтобы всегда включать их. Таким образом, вы можете иметь безопасные _и_ правильные типизации для большинства случаев использования и особенно при написании библиотек, имея при этом доступ к дополнительным преимуществам перезаписи глобальных Promise в приложениях.

Если вы считаете, что эта идея хороша, и если у вас осталось свободное время, было бы здорово, если бы вы могли создать PR. Я уверен, что многие были бы очень рады такому вкладу. 🙂

Я формулирую это таким образом, потому что в настоящее время я не в состоянии писать эти типизации из-за нехватки времени и не нуждаюсь в таких типизациях в данный момент. Надеюсь, вы это понимаете.

@lhecker Я думаю, что могу с тобой согласиться. Потому что, если у нас будет глобальное переопределение Promise на bluebird, мы взломаем только компилятор машинописного текста, но не реальный мир. Например, с переопределенным Promise typescript будет думать, что fetch возвращает значение bluebird:

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

Без обертывания fetch в Promise.resolve вы не получите, например, метод .finally на result :

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

Итак, я думаю, что явное импортирование bluebird при каждом использовании является лучшим решением:

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

Я собираюсь реорганизовать свой код.

Спасибо за ваш ответ, @lhecker .

Я согласен со всем, что ты сказал. И мне нравится решение @Strate по обертыванию стороннего кода с помощью метода Promise.resolve() , чтобы преобразовать обещание es6 в Bluebird (или Bluebird в Bluebird, потому что я хочу, чтобы обещание Bluebird было глобальным во время выполнения, поэтому я не не нужно полагаться на сторонний код для правильной обработки своих ошибок, но это не относится к делу).

Похоже, я не знал, как это сделать правильно. Я думаю, что другим было бы полезно больше документации о том, как справиться с этой проблемой, потому что каждый, кто приходит из мира браузерного программирования (в отличие от: из nodejs/typescript), сталкивается с этим после того, как они:

  1. npm install <absolutely everything that uses es6 promise>
  2. npm install bluebird @types/bluebird
  3. используйте сторонний код с машинописным текстом

Мне также было бы полезно иметь этот раздел «Как использовать в случае, если сторонний код набирается против обещания es6» где-нибудь задокументирован для дальнейшего использования. т.е. имея

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

и

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

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

в файле readme или в блоке документов в верхней части файла bluebird.d.ts.

Что вы думаете?

Я завершил переход от глобального переопределения Promise к bluebird и обнаружил некоторые проблемы, когда сторонние библиотеки возвращают обещание ES6, которое считалось обещанием bluebird. Таким образом, этот шаг также очистил мою кодовую базу. Я бы порекомендовал всем отказаться от глобальной перегрузки Promise . Ваше здоровье :)

Я понимаю modules > globals , но давайте скажем ради аргумента (и/или реальности), что я работаю над большим браузерным SPA, и мне было поручено использовать Bluebird в качестве нашего полифилла Promise.

Я пробую исправление bluebird-global.d.ts , предложенное @lhecker , с содержимым из @d-ph:

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

Я установил его через typings , который сгенерировал мой 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> {} }
}

Однако, когда я пытаюсь все собрать, TypeScript (v1.8.2) жалуется:

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.

Я взглянул на пример MS для global-modifying-module.ts
https://www.typescriptlang.org/docs/handbook/declaration-files/templates/global-modifying-module-d-ts.html

И проблема TS, связанная с этим сообщением об ошибке
https://github.com/Microsoft/TypeScript/issues/6722

Но я в недоумении, что я должен делать. Кто-нибудь может помочь?

Привет.

Не могли бы вы проверить мой репо, который показывает проблему и решение? ссылка . Меня это вполне устраивало, пока я не решил обернуть все промисы от третьих лиц в конструктор промисов bluebird, что я и делаю сейчас. Не могли бы вы подтвердить, что после того, как вы выполните шаги в файле readme, вы не сможете скомпилировать, но после того, как вы раскомментируете

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

он компилируется?

Имейте в виду, что в моем репо используется TS 2 (который теперь стабилен), а вы сказали, что используете 1.8.2. Просто проверьте, что происходит, когда вы обновляете TS до 2.

Наконец, у меня возникли некоторые проблемы с помещением моего решения в мой глобальный файл d.ts . В итоге я добавил его в каждую точку входа моей компиляции веб-пакета, что решило проблему (теперь это имеет для меня смысл). Я не знаю ваших настроек js, но не могли бы вы попробовать поместить мое исправление в каждый файл, который не работает во время компиляции (или хотя бы в один из них), и проверить, помогает ли это?

Я также хочу иметь возможность «зарегистрировать» реализацию Bluebird Promise как глобальную Promise . Прочитал всю ветку, но не понял какой-то части. Часть, предполагающая, что сторонние библиотеки по-прежнему будут возвращать нативную (например, не-Bluebird) реализацию Promise . Как это могло быть, когда этот сторонний код в какой-то момент вызывает конструктор Promise ( new Promise(...) ), который был заменен реализацией Bluebird на глобальном уровне?

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

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

Разве это не должно работать нормально, поскольку я включил Bluebird, который заменил нативную реализацию Promise ?

Наконец, если я делаю эту полную глобальную замену во время выполнения, я должен иметь возможность сообщить об этом TypeScript, чтобы во время компиляции все промисы также были заменены на Bluebird.

Что мне не хватает?

Если вы используете дистрибутивную версию bluebird (что вы и делаете), то сторонние библиотеки будут использовать Bluebird, поскольку глобальное обещание теперь называется Bluebird. Вы правильно понимаете эту часть. Люди упоминают об обратном, потому что говорят об использовании Bluebird с помощью node.js.

Вся эта ветка посвящена не столь очевидному способу компиляции ts с таким предположением (глобальный Promise — это Bluebird). Если вам удалось это сделать (например, с помощью declare global {} ), то все готово.

@d-ph Но мой вывод заключается в том, что я должен делать это в каждом файле *.ts , а не только один раз - это правильно? Может быть, краткое изложение окончательного «решения» этой проблемы было бы хорошо. :)

Да, видите ли, здесь нет простого just copy&paste this line to your X file and everyone and their dog are happy now решения. Или, по крайней мере, я не в курсе.

Последний раз, когда я проверял вас:

  1. скопируйте и вставьте строку import * as Bluebird from 'bluebird'; declare global { export interface Promise<T> extends Bluebird<T> {} } в каждую точку входа в файл *.ts или
  2. оберните каждое обещание, возвращенное из стороннего кода, в функцию-конструктор Bluebird. Во время выполнения он ничего не будет делать (за исключением ненужных накладных расходов во время выполнения), а во время компиляции он порадует TS.

Опять же, решение 1. требует размещения этого кода только в файлах точек входа, а не в каждом файле. По крайней мере, у меня это сработало (webpack + awesome-typescript-loader).

Если вы найдете другое решение, которое буквально требует от разработчиков помещать только 1 строку в 1 файл, пожалуйста, поделитесь с сообществом ;p

Спасибо @d-ph - можете ли вы подтвердить, что вы подразумеваете под «каждой точкой входа *.ts файл»?

Ага. Файл "точка входа" .ts - это файл .ts, который вы загружаете в свой html с помощью тега <script> . Другими словами, это файл, с которого начинается компиляция.

Только сейчас, после быстрого поиска в Google, я нашел это (не утруждайте себя чтением). Суть в том, что если вы вручную добавите global { export interface Promise<T> extends Bluebird<T> {} } в bluebird.d.ts, то вам не нужно упоминать об этом снова где-либо еще. У меня нет времени тестировать его прямо сейчас, но я протестировал его с помощью тестового репозитория github, который я создал, и, похоже, он работает.

Другими словами, скачайте bluebird.d.ts и измените это:

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

к этому:

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

Очевидно, это не идеально, особенно если вы используете @types/bluebird (которую вам нужно удалить сейчас, потому что вы будете использовать свой собственный взломанный bluebird.d.ts), но что ж...

Итак, у меня уже есть файл _stubs.d.ts , к которому я добавил следующее, и я намного ближе. Больше нет жалоб на то, что finally не существует на Promise , но по какой-то причине я все еще получаю сообщения об ошибках о том, что delay не существует на Promise . Мне не пришлось редактировать bluebird.d.ts . Будет исследовать, но это может быть отличным решением!

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

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

Редактировать: моя проблема с delay заключалась в том, что я вызывал ее статически, например, Promise.delay(2000) .

С решением, которое я опубликовал выше, я получаю эту ошибку, когда моя функция возвращает Promise<T> :

ошибка TS2322: введите «Bluebird' нельзя присвоить типу 'Promise'.

Я предполагаю, что это потому, что теперь, когда я заменил Promise на Bluebird , всякий раз, когда я использую then и т. д., возвращаемое значение вместо этого равно Bluebird<T> из Promise<T> .

Вот моя окончательная версия этого хака. Мне не нравится делать это, но мне это нравится больше, чем другие варианты. По сути, мне приходится повторять то, что я использую в интерфейсе, меняя тип возвращаемого значения на Promise вместо Bluebird . Это прямая копия и вставка из файла определения Bluebird, отличного от этого.

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

Было бы здорово иметь официальное определение bluebird-global или bluebird-override , которое очень похоже на существующее определение, но везде использует Promise вместо Bluebird .

Рад, что вы смогли найти решение.

Просто для полноты: как сказал @ProTip , используя bluebird-2.0.d.ts JustWorksTM. Просто установите его с npm install @types/[email protected] и добавьте в compilerOptions.types tsconfig.json:

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

Любые различия между этим .d.ts и текущей версией Bluebird (т.е. 3.x) я рекомендую взломать вручную.

Под JustWorksTM я подразумеваю: он работает для:

а) цель es5
б) цель es6
c) цель es5 с библиотекой core-js

независимо от того, использует ли кто-то настройку сборки (webpack + awesome-typescript-loader) или нет. Кроме того, PhpStorm IDE совершенно не путается.

Сегодня я потратил некоторое время на изучение этой проблемы и фактически создал/обновил эти два билета в Microsoft/TypeScript: https://github.com/Microsoft/TypeScript/issues/10178 и https://github.com/Microsoft/TypeScript . bluebird-global.d.ts . Чтобы избежать дублирования кода, я обнаружил, что это сработает:

// bluebird-global.d.ts

import * as Bluebird from "bluebird";

export as namespace Promise;
export = Bluebird;

при условии, что два вышеупомянутых тикета решены или найдены обходные пути. А пока я рекомендую использовать bluebird-2.0.d.ts при написании кода для браузера.

@ДжошМакКаллоу

Как это могло быть, когда этот сторонний код в какой-то момент вызывает конструктор Promise (new Promise(...)), который был заменен реализацией Bliebird на глобальном уровне?

Очень легко. Попробуйте в консоли браузера (предпочтительно 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!

Привет. Я делал это, и это довольно гладко. Прежде всего, не полагайтесь на bluebird для библиотечных проектов. Для проекта приложения импортируйте bluebird, но не типизацию. В точке входа вашего приложения сделайте следующее:

global['Promise'] = требуется('синяя птица')

Это заменит глобальный объект обещания для приложения и всех включенных библиотек.

Привет, ребята,

@types/bluebird-global теперь доступен. Эти типизации используют @types/bluebird@^3.0 под капотом и позволяют вам использовать методы bluebird в глобальном Promise (т.е. компиляция ts не завершается ошибкой).

Пожалуйста, прочтите это , чтобы узнать, как его использовать.

Круто, спасибо @d-ph!

@d-ph Спасибо за @types/bluebird-global . Нужно ли мне выполнять какой-либо импорт и переназначение в моем проекте, чтобы использовать bluebird в качестве замены глобального обещания?

npm install --save-dev @types/bluebird-global , а затем следуйте инструкциям, которые я включил в наборы: ссылка (ссылка обновлена ​​2017-04-02). Это само по себе должно помочь (т.е. не требуется никакого ручного импорта/переназначения).

В качестве примечания: вам больше не нужно упоминать @types/bluebird в вашем package.json::devDependencies , потому что это подразумевается автоматически.

Обновление по ссылке в предыдущем комментарии: ссылка

@MichaelTontchev @d-ph есть ли шанс, что мы сможем добавить интерфейс Promise.Inspection в bluebird-global ?

Привет @ksnyde.

Пожалуйста, поговори со мной. Я сопровождающий.

Не могли бы вы подтвердить, что вы имеете в виду этот Promise.Inspection ?

Все методы из этого интерфейса доступны через bluebird-global . Т.е. будет скомпилировано следующее:

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

Так что мне кажется, что вы просите, чтобы это было выставлено как Promise.Inspection from bluebird-global .

Не могли бы вы сказать мне, является ли это серьезной неудачей для вас, если вместо этого вы используете следующее:

import * as Bluebird from "bluebird";

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

}

Поскольку вы используете bluebird-global , вы также можете импортировать исходные типы bluebird без каких-либо явных devDependencies .

Я бы предпочел не расширять глобальный Promise дальше без веских причин, потому что заставить эти типизации работать со стандартными типами Promise из lib.d.ts — тонкое искусство. Я действительно рекомендую обращаться к этому интерфейсу напрямую из типизаций bluebird , потому что однажды гуру JavaScript могут добавить к стандарту Promise.Inspection , что нарушило бы типизацию bluebird-global , что, в свою очередь, создавать ненужные проблемы для конечных пользователей.

Кроме того, даже если бы я добавил интерфейс, вам пришлось бы ждать неопределенное количество времени, пока он будет объединен с master , потому что в эти дни сопровождающие DT заняты PR.

Ваше здоровье.

Я действительно использовал подход, который вы обсуждали в настоящее время, и это адекватный обходной путь. Или, может быть, "обойти" - неправильная номенклатура.

Мое понимание/восприятие заключалось в том, что идея bluebird-global заключалась в том, чтобы раскрыть надмножество функций Promise, которые предоставляет bluebird, и в этом случае, как пользователь, я _буду_ ожидать, что Bluebird.Inspection будет выставлен на Promise.Inspection . Однако, если ваше намерение состоит в том, чтобы просто убедиться, что официальная поверхность API Promises использует Bluebird, то я думаю, что этот «обходной путь» на самом деле является подходящим долгосрочным решением.

Хотя я предпочитаю свою интерпретацию, я буду в порядке, используя представленное здесь решение, если это необходимо.

Хотя я, конечно, понимаю, откуда вы исходите со своими ожиданиями, основная причина, по которой я создал bluebird-global , заключалась в том, чтобы сообщить TypeScript, что промисы, созданные и возвращенные из стороннего кода, на самом деле являются экземплярами промисов Bluebird, к которым не было другой _не раздражающей_ альтернативы, кроме как выставить все экземпляры Bluebird и статические методы на глобальном символе Promise. Как я упоминал ранее, это делается не просто class Promise<T> extends Bluebird<T> {} (хотя изначально так и было), а скорее тщательно исправленным глобальным символом Promise. И, как я уже упоминал, я предпочел бы избежать необходимости поддерживать что-либо, для чего есть известная альтернатива, которая буквально не заставляет вас рвать на себе волосы.

Извините, что сказал "нет" этому. Если будет больше людей, запрашивающих эту конкретную функцию, я пересмотрю ее добавление. Я не хочу звучать здесь авторитетно — это проект с открытым исходным кодом, и любой, вероятно, мог бы внедрить эту функцию. не превышает затрат на его содержание.

Ваше здоровье.

имеет смысл. спасибо за размышления о вашем подходе.

@d-ph имеет ли для вас какое-либо значение следующее... Я получаю сообщение об ошибке, в котором говорится, что "отражать" не является функцией в приведенном ниже коде:

image

И хотя в Intelisense редактора правильно определяется, что свойство p в функции сопоставления является обещанием Bluebird и имеет расширенную поверхность API, которую можно найти только в Bluebird (по сравнению с Promise).

image

Я просто не могу сделать из этого головы или решки. Я заметил, что когда я проверяю _type_ переменной итератора карты, она отображается как:

image

Я предположил, что именно поэтому я получаю ограниченную поверхность API, определенную bluebird-global , но я не знаю, почему она не разрешается правильно.

Привет.

Я не могу воспроизвести :/ Эти фрагменты кода работают в моей настройке.

Прежде всего. вы не используете bluebird-global в своей функции allSettled() . Вы печатаете напрямую, используя Bluebird . Это не проблема, но, возможно, вы не знали об этом. В следующем фрагменте используется bluebird-global :

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

    return reflections;
}

Т.е. этот сниппет вводит в глобальный Promise (который пропатчен методами Bluebird в bluebird-global.d.ts ). Как я уже сказал: это для вашего сведения, если вы не знали об этом, потому что и ваш, и мой фрагменты работают одинаково.

Вернемся к проблеме отсутствия методов Bluebird. Мое предположение таково: вы не заменяете глобальный Promise на Bluebird во время выполнения, а затем запускаете allSettled() с промисами, созданными с использованием глобального промиса вместо Bluebird.

Позвольте мне показать вам мой код и несколько скриншотов.

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: Обещание в Array.map() — это Bluebird (вы можете сказать по наличию свойств подчеркивания, таких как: _bitField )_

image
_Рис. 2: Promise.reflect действительно определен в цикле_

Не могли бы вы установить точку останова js, как я, в Chrome Dev Tools и посмотреть, что такое promise внутри .map ? Еще лучше: не могли бы вы просто ввести Promise в консоли и посмотреть, получите ли вы следующий вывод:
image

Если вы получите следующий вывод, значит, вы НЕ заменили глобальный Promise на Bluebird во время выполнения:
image

В таком случае вам нужно это сделать. Например, как я в вашем входном файле.

Наконец, небольшой отзыв разработчиков: лично я бы использовал Promise<T>[] вместо Array<Promise<T>> . Но это, конечно, на усмотрение разработчиков. Я только что пришел из C, где нет шаблонов и где разработчики используют оператор доступа к массиву для определения типов.

Ваше здоровье.

Большое спасибо за такое подробное объяснение. Одна вещь, в которой я не был уверен, заключалась в том, на что указывает следующее:

import 'expose-loader?Promise!bluebird';

@ d-ph аааа, мне не хватало одной строчки выше. Без вашей помощи никогда бы туда не попал! Это может быть только я, но я думаю, что было бы полезно, если бы в тексте README была ссылка на использование Expose Loader .

Хотя, честно говоря, я все еще не на 100%, если для этого потребуется использование веб-пакета? Моя цель не браузер, а просто функция узла.

На самом деле, похоже, что он работает не полностью, поскольку я получаю следующую ошибку при попытке выполнить файл (в редакторе до запуска нет ошибок):

Ошибка: не удается найти модуль «expose-loader? Promise! bluebird»

Вы правы, expose-loader — это веб-пакет (загрузчик веб-пакетов). Поэтому, если вы не позволите веб-пакету обработать этот оператор import , он не запустится (т.е. вы получите ошибку «Не удается найти модуль»).

Если вы не можете/не используете webpack, вам нужно найти другой способ сделать глобальное обещание Bluebird во время выполнения. У меня нет большого опыта работы с узлом, но я нашел это только сейчас: https://github.com/petkaantonov/bluebird/issues/1026 (переопределение Promise в узле).

Я рекомендую узнать, как использовать async/await в последнем узле 7. Вам повезло жить во времена, когда это доступно для разработчиков.

Что касается упоминания об использовании expose-loader в README: я уже указывал в начале файла d.ts , что это работа разработчиков по замене Promise на Bluebird во время выполнения: ссылка . Так как существует так много способов сделать это, я на самом деле не упомянул ни один из них. Имейте в виду, что expose-loader — это просто эквивалент запуска window.Promise = Bluebird . Что ж, надеюсь, Google проиндексирует мой ответ, так что люди не будут больше искать возможные варианты слишком долго ;p

@d-ph с нетерпением жду встроенного async-await, но все эти функции есть в AWS Lambda, поэтому я пока привязан к узлу 6.10.x. В будущем проходе этих функций я, вероятно, все равно переключусь на async-await и заставлю Typescript транспилировать его в ES2015, но пока не хочу вводить это.

В любом случае, спасибо за ссылку @d-ph, я попробую этот подход. Да, и, если кому-то интересно, окончательная версия этих функций (которые очень удобны в обещанной стране):

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

Доступна полная информация об интеллисинхронизации/типе, что очень приятно.

@ d-ph, надеюсь, все в порядке, если я оживлю это с соответствующим вопросом ...

Когда я пытаюсь использовать bluebird-global, я, кажется, получаю немного различий в определениях как в then , так и catch . Я вижу, что они обрабатываются специально в bluebird-global, но, похоже, это ограничивает меня стандартным API-интерфейсом Promise вместо получения bluebird. Например:

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

терпит неудачу, поскольку catch ожидает только 1 аргумент.

И еще вопрос:

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

Не удается с:

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

Они должны работать или я что-то не так делаю? Они работают во время выполнения, они просто не проверяют тип.

Спасибо!

Привет,

Что касается .catch(Error, function) , вы правы, говоря, что .then , .catch (и другие) обрабатываются иначе, чем остальные функции Bluebird. Однако конкретное переопределение .catch(Error, function) включено в bluebird-global . Я дважды проверил, и я могу скомпилировать:

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

Я проверял с TS 3.0.1 и 2.9.2. Я не знаю, почему вы можете столкнуться с проблемой здесь. Возможно, в вашем проекте TS есть что-то конкретное, что переопределяет глобальное обещание после bluebird-global . Я не знаю. Возможно, попробуйте сузить круг причин проблемы, начав с очень простого проекта TS, а затем добавив больше зависимостей из вашего текущего проекта, и посмотрите, в какой момент он сломается.

О другой проблеме: я не знаю, почему это не работает. И да, это должно работать. Пожалуйста, создайте для этого проблему на github, и мы пойдем оттуда. Вот несколько фактов о проблеме:

  1. Все, что bluebird-global делает с .map() , просто повторно использует определение типа bluebird.d.ts 's .map() . Другими словами, дефект не должен быть связан с типизацией bluebird-global .
  2. Упомянутая вами строка не работает на Promise.map() , но работает на Bluebird.map() :
import Bluebird = require('bluebird');

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

Promise.resolve([3]).map((n: number) => true); // fails
  1. Потратив некоторое время на расшифровку проблемы с машинописным текстом (по сути: почему TS делает вывод, что параметр n должен быть {} ), я пришел к выводу, что bluebird.d.ts также не должен работать, но работает по неизвестной мне причине. Короче говоря, после удаления всех слоев абстракций набирается .map() :
map<U>(
    mapper: (
        item: U,
        index: number,
        arrayLength: number
    ) => U | PromiseLike<U>,
    options?: Bluebird.ConcurrencyOption
): Bluebird<T extends Iterable<any> ? U[] : never>;

В нем говорится, что возвращаемый тип функции mapper должен быть таким же (или обещанием того же самого), что и тип item . В вашем примере тип элемента — number , а возвращаемый тип — boolean . Я не могу понять, почему это компилируется при прямом использовании Bluebird , но не при использовании глобального промиса. Кстати, это все еще не работает, когда я меняю тип возвращаемого значения в вашем примере на любое число. Однако это работает, когда я говорю, что item может иметь тип any . Что-то не так с bluebird.d.ts 's type IterableItem<R> и его использованием в этом контексте.

@d-ph


Редактировать:

Я снова проверил «неслоистую» форму $# map() 28$#$, и функция mapper не типизирована, чтобы иметь тот же тип возвращаемого значения, что и тип item (я думал, что это был). Виноват.

Все, что bluebird-global делает с .map(), просто повторно использует определение типа bluebird.d.ts .map().

Является ли проблема в том, что когда вы копируете тип из универсального класса Bluebird<R> , он по умолчанию принимает значение {} для R , поскольку он не может вывести его из родителя?

Интересно, сработает ли map: typeof Bluebird<T>.prototype.map ? (это я еще не пробовал)

ВАЖНЫЙ:
Если вы используете @types/bluebird-global , удалите из своих зависимостей @types/bluebird , как сказал @d-ph

npm install --save-dev @types/bluebird-global , а затем следуйте инструкциям, которые я включил в наборы: ссылка (ссылка обновлена ​​2017-04-02). Это само по себе должно помочь (т.е. не требуется никакого ручного импорта/переназначения).

В качестве примечания: вам больше не нужно упоминать @types/bluebird в вашем package.json::devDependencies , потому что это подразумевается автоматически.

Наличие обоих вызывало несоответствие между типами, возвращаемыми @types/bluebird , и моим глобальным обещанием ( @types/bluebird-global )

Была ли эта страница полезной?
0 / 5 - 0 рейтинги