Расширение класса из внешней библиотеки с помощью статической функции безопасным для типов способом в TypeScript

#javascript #typescript #static #rxjs #rxjs5

#javascript #typescript #статический #rxjs #rxjs5

Вопрос:

Я хочу расширить Observable класс rxjs5 с помощью статической функции. Я могу сделать это простым JavaScript:

 var myStaticFn = function() { /* ... */ };
Observable.myStaticFn = myStaticFn;
  

это работает, но в TypeScript я не могу получить доступ Observable.myStaticFn , поскольку свойство myStaticFn неизвестно в классе Observable .

Как мне объявить / дополнить класс модуля Observable rxjs5, чтобы я мог получить доступ к своей функции безопасным для типов способом?

Примечание: В качестве отправной точки ниже показан пример, как расширить нестатическую функцию до наблюдаемой (например, для создания пользовательского оператора rxjs), и это полностью работает, но это не то, что я хочу!

 function myOperator(this: Observable<any>): Observable<any> = function(){ /*...*/ };
Observable.prototype.myOperator = myOperator;

declare module "rxjs/Observable" {
    interface Observable<T> {
        myOperator: typeof myOperator;
    }
}
  

Вышеуказанное работает, потому declare что синтаксис TypeScript позволяет мне рассматривать Observable его как интерфейс, а интерфейсы могут быть расширены / объединены. Но в TypeScript нет способа объявить статическую функцию в интерфейсе.

Также невозможно получить производное от Observable класса, скажем ExtendedObservable , потому, что каждый пользователь моего кода должен был бы использовать ExtendedObservable тип вместо Observable типа во всем проекте, и концепция дополнительно терпит неудачу, если я хочу использовать разные статические методы для наблюдаемого, в зависимости от импортированных модулей.

Комментарии:

1. Сейчас существует открытая проблема отсутствия статического модификатора в интерфейсах github.com/Microsoft/TypeScript/issues/14600 .

Ответ №1:

Я обнаружил это сам, глядя на реализацию статического .from() расширения в исходном коде RxJS:

 import { myStaticFn as myStaticFnStatic } from "./myStaticFn";

declare module "rxjs/Observable" {
    namespace Observable {
        let myStaticFn: myStaticFnStatic;
    }
}
  

Обратите внимание, как я импортирую myStaticFn , но привязываю его локально к имени myStaticFnStatic — это необходимо, иначе вы получите ошибку компилятора.

Комментарии:

1. Dyna это именно мое решение, как я уже упоминал, Microsoft изменила термин «внутренний модуль» на «пространство имен».

Ответ №2:

В файле определения для rx у вас есть определение для наблюдаемого класса и ObservableStatic .
Если вы хотите добавить статические функции, вам необходимо увеличить ObservableStatic :

 declare module "rxjs/Observable" {
    interface ObservableStatic {
        myOperator: typeof myOperator;
    }
}
  

Комментарии:

1. К сожалению, это относится только к rxjs v <= 4.0. Я специально ищу решение RxJS v5 (в настоящее время в rc-1). В rxjs5 больше нет класса ObservableStatic.

2. Кажется, что новая версия полностью переписана в typescript, и в ней нет файла определения, вы просто, вероятно, используете исходный код правильно?

3. Нет, я использую модуль CommonJS, который имеет аннотацию типа для каждого файла * .ts, т.Е. node_modules/rxjs/Observable.d.ts Которые Используются при компиляции TS.

Ответ №3:

Вам нужно объявить модуль с тем же именем интерфейса. Внутри модуля вы пишете статические функции. Это лучшая практика в TypeScript. Это называется «слияние модулей».

Ссылка: Страницы Pro TypeScript #40 #41

Только одно замечание: Microsoft изменила термин «внутренний модуль» на «пространство имен» с момента написания этой книги: Пространства имен

Комментарии:

1. можете ли вы добавить некоторый код, возможно, используя Observable примеры из моего вопроса?

2. В этом случае расширение модуля не работает, потому что вы не можете объявить функцию в интерфейсе как статическую

3. @Dyna Мне жаль, что у меня недостаточно времени для создания такого кода и его тестирования. Просто изучите тему «слияния модулей» в книге, на которую я ссылался. Он начинается в конце страницы 40, и он очень короткий и по существу. Поскольку у вас будет вложенный модуль, это может быть немного сложно, но это решение.

4. После прочтения страницы 40/41 раздела Module Merging я думаю, что упомянутый подход работает только при использовании внутренних модулей в TS. При использовании внешних модулей я не могу создать module Observable {} , так как это всегда будет приводить к ошибке «Объявление импорта конфликтует с локальным объявлением ‘Observable’ »

5. @Dyna это будет работать нормально, только одно: с момента написания этой книги Microsoft изменила название «внутренний модуль» на «пространство имен». Поэтому вместо того, чтобы писать «наблюдаемый модуль», напишите «Наблюдаемое пространство имен». Вы не можете создать этот модуль снова, но вы можете использовать то же пространство имен, которое этот модуль создал автоматически: typescriptlang.org/docs/handbook/namespaces.html