Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I handle optional peer dependencies when publishing a TypeScript package?

When publishing a TypeScript package to npm that provides a function that accepts input from either one peer dependency or another, how do I define the optional peer dependencies?

import { ExternalFoo } from 'foo';
import { ExternalBar } from 'bar';

export const customPackage = (source: ExternalFoo | ExternalBar) => {
    /* ... */
}

How do I prevent people using my package from getting errors when one of the two options is missing?

like image 579
Paul Gerarts Avatar asked Jan 27 '19 20:01

Paul Gerarts


People also ask

Do peer dependencies install automatically?

Unmet Dependencies When Testing When you use peer dependencies, npm will not automatically install those dependencies (see comments above in respect to npm version 7).

What's the difference between dependencies DevDependencies and peerDependencies in package JSON file?

A dependency is a library that a project needs to function effectively. DevDependencies are the packages a developer needs during development. A peer dependency specifies that our package is compatible with a particular version of an npm package.

Does yarn automatically install peer dependencies?

Automatically installs project's peerDependencies (as devDependencies).

How to prepare a module with peer dependencies?

Fortunately prepare is called after dependencies installation and only when you are developing so it won't install the peer dependencies when a user install your module. Then add the prepare script in your module's package.json and call install-peers-cli in it:

What are peer dependencies in Python?

Peer dependencies are a specific kind of dependencies really useful for reusable modules: Ask user to install a dependency your module needs to work without specifying a version in particular The problem with peer dependencies is npm and yarn don't install them at all.

Why does Browserify-typescript-extension need to be installed with devdependencies?

Our package exposes declarations from each of those, so any user of our browserify-typescript-extension package needs to have these dependencies as well. For that reason, we used "dependencies" and not "devDependencies", because otherwise our consumers would have needed to manually install those packages.

What is a peer dependency in NPM?

Peer Dependencies are used to specify that our package is compatible with a specific version of an npm package. Good examples are Angular and React. To add a Peer Dependency you actually need to manually modify your package.json file. For example, for Angular component library projects, I recommend adding angular/core as a peer dependency.


2 Answers

Since Typescript 3.8 you can use the following syntax:

import type { ExternalFoo } from "foo";

So if you're just using that library for type information, you probably don't have to list it as a dependency or optionalDependency anymore. You might prefer to leave it as a peerDependency so that if your users will have those dependencies, they'll be on versions compatible with your library. Naturally, adding as devDependency is useful too.

The import will live on the generated d.ts files only, but not on .js transpiled code. One problem, though, is that if users don't have that library installed, that type becomes any and this might mess up your own typing a bit. For example, if foo is not installed your function becomes

customPackage = (source: any | ExternalBar) =>
// equivalent to customPackage = (source: any) =>

And for this particular type annotation, it sucks because even if I have bar installed, it won't be used in that type checking. So there is a way to not depend on that library anymore, but it doesn't solve the hardship of writing type annotations that won't break down if that type is not there.

Edit: please take a look at this answer on how to deal with missing types.

Reference

like image 174
villasv Avatar answered Sep 20 '22 13:09

villasv


Combining all the current answers, here's the best solution I've found for current versions of TypeScript (as of late 2021):

// @ts-ignore -- optional interface, will gracefully degrade to `any` if `foo` isn't installed
import type { Foo } from "foo";
import type { Bar } from "bar";

// Equates to `Bar` when `foo` isn't installed, `Foo | Bar` when it is
type Argument = any extends Foo ? Bar : (Foo | Bar);

export function customPackage(source: Argument): void {
  ...
}

You can try it out yourself. If foo is installed, the exported method will take Foo or Bar arguments, and if it isn't, it will only accept Bar (not any).

like image 38
Coderer Avatar answered Sep 20 '22 13:09

Coderer