Typescript: How to use types of an optional external library

Viewed 220

I am trying to use the Observable type from rxjs as return value of observe methods in my own library, but I don't want add the rxjs dependency to my project. It is optional and only needed if the user of my library actually uses those particular methods, in which case they would have to install rxjs into their project themselves.

I can't find the right typescript syntax though.

The following code summarizes how I am using this now, with a TypeScript error if rxjs is not installed in projects using my library: "Cannot find module 'rxjs' or its corresponding type declarations.ts(2307)"

I've isolated the Observable import into a single file, so I can reference it from multiple classes (files) that implement an observe method. Also, note that I am using my own type definition files instead of generating them with tsc to prevent exposal of private and/or experimental API's in my library.

types/optional-observable.d.ts:

export { Observable } from 'rxjs'; // Error if rxjs is not in project, needs fixing

types/data-reference.d.ts:

import { Observable } from './optional-observable'; 
export class DataReference {
   // many other properties and methods
   
   observe(): Observable<any>
   observe<T>(): Observable<T>
}

In the implementation of the optional observables, I use the following code:

src/optional-observable.ts:

export function getObservable() {
    try {
        const { Observable } = require('rxjs');
        if (!Observable) { throw new Error('not loaded'); }
        return Observable;
    }
    catch(err) {
        throw new Error(`RxJS Observable could not be loaded. Add it to your project with: npm i rxjs`);
    }
}

src/data-reference.ts:

import { getObservable } from './optional-observable';
export class DataReference {
   observe() {
      const Observable = getObservable(); // throws if rxjs not installed, which is ok
      return new Observable(observer => {
         // ....
      });
   }
}

To summarize:

  • I have methods in my library that require rxjs Observables to work, but are optional
  • If the user of my library uses those methods, they have to add the rxjs dependency to their project
  • If rxjs is installed, autocomplete becomes available for my observe methods in the IDE
  • If rxjs is not installed, there must be no compile time errors.
1 Answers

While typing my question, I realized it might just be possible for the TypeScript compiler to ignore the absense of rxjs using a linting style comment to have tsc ignore the error. Turns out all I have to do is add // @ts-ignore to the line above the export:

types/optional-observable.d.ts:

// @ts-ignore: rxjs dependency is optional and only needed when using methods that require them
export { Observable } from 'rxjs';

The docs at https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-6.html#suppress-errors-in-ts-files-using--ts-ignore-comments state:

Suppress errors in .ts files using // @ts-ignore comments

TypeScript 2.6 support suppressing errors in .js files using // @ts-ignore comments placed above the offending lines.

A // @ts-ignore comment suppresses all errors that originate on the following line. It is recommended practice to have the remainder of the comment following @ts-ignore explain which error is being suppressed.

This fixes my issue. I thought I'd share it anyway for others running into the same issue!

Related