How to make an NPM package that uses global types but doesn't augment the project using it?
Asked Answered
S

1

2

The Question:

Any project that reuses a lot of types in many different files can make use of types defined in a script file. Those types are visible globally across the project and don't need to be imported, see the official handbook:

In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).

I've used these global types in my project and they worked fine. But then I decided to turn the project into a NPM module. The "global" types of that module should only be visible for the module itself.

First I pusblished the module as it was, without adding any <reference/>s and typeRoots. However when imported the module into a different project, TypeScript couldn't find the global types and wouldn't compile.

Then I added <reference path="types.ts" /> to all files in the module that used the global types, then I pushed again. To my surprise, this made the types available not only to the module, but also to the whole project that used it, causing conflicts between local types and the types of the module.

(Note that both versions of the module that I published were perfectly compillable on their own, the problems began when I installed the module using npm i in a different project.)

Is there any way to make the module's "global" types only available in the module?


Structure of my module:

.
├── dist
│   ├── index.d.ts
│   ├── index.js
│   ├── foo.d.ts
│   ├── foo.js
│   ├── types.d.ts
│   └── types.js
├── package.json
├── src
│   ├── index.ts  ← contains import/export
│   ├── foo.ts    ← contains import/export
│   └── types.ts  ← contains only types, no import/export
└── tsconfig.json

For the first publish, index.ts and foo.ts contained only regular code – that caused the the types not to get recognized when the project. When I published the module for the second time, I added ///<reference path="types.ts"/> to both index.ts and foo.ts. This got translated into path="types.d.ts" in the compiled files index.d.ts and foo.d.ts.

package.json:

{
  "name": "@foo/foo",
  "version": "1.0.0",
  "main": "dist/index.js"
}

tsconfig.json:

{
  "include": ["./src"],
  "compilerOptions": {
    "moduleResolution": "classic",
    "outDir": "./dist",
    "module": "commonjs",
    "declaration": true
  }
}
Sweetandsour answered 16/4, 2019 at 16:46 Comment(0)
B
1

If a dependency needs some types, I feel like consumers should get those types, too, so achieving this may not be easy.

You could try defining your global types in a separate library (refer to your previous question for ways to do that; this time the library could be as simple as my-global-library/index.d.ts, unless you need to import typings from sibling dependencies – in which case you may consider spinning those off into a shared library of their own), then installing that library as a devDependency of your NPM package. Any project that consumes your NPM package would then not install the devDependency.

Beetle answered 17/4, 2019 at 9:13 Comment(1)
The problem is not that consumers get access to the module's types, that's advisable, but that the types are global and cause conflicts with other global types present in their project. Your solution sounds good, but I'm afraid that the project wouldn't compile because of the missing types, I'll try it out.Sweetandsour

© 2022 - 2024 — McMap. All rights reserved.