How do I handle optional peer dependencies when publishing a TypeScript package?
Your situation is a case that currently TypeScript does not support well.
Let's first summarize your situation:
foo
andbar
is your optional dependency, meaning you expect your consumer will use one of them along with your library.- You are only using the type information from those libraries, meaning you don't have any code dependency and don't want to add them as
dependencies
in yourpackage.json
- your
customPackage
function is public.
Because of point 3, you need to include the type in your library typings, meaning you need to add foo
and bar
as dependencies. This contradicts with point 1 and 2.
If the typings of foo
and bar
comes from DefinitelyTyped (i.e. from package @types/foo
and @types/bar
), then you can add them as your dependencies
in package.json
. That will solve the problem.
If the typings of foo
and bar
are distributed with the libraries themselves, you have to either include the libraries as dependencies
(which you don't want),
or create a replicate of the types ExternalFoo
and ExternalBar
yourself.
This means you will cut yourself off from depending on foo
and bar
.
Another way is looking at your library closely and sees if there is any harm in including the foo
and bar
as dependencies. Depending on the nature of your library, it might be not as bad as you think.
Personally, I typically will go for declaring the types myself. JavaScript is a dynamic langauge to begin with.
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