How to link a npm package with its types

You just created a package / library written in TypeScript or Javascript and now you wanna publish it to NPM along with its types (typescript definitions) ensuring that type-hinting is available to everyone who uses your packages, then this little post is for you <3

Linking a NPM package with its types

Your package could have been written in typescript or in javascript.
Lets deal with packages written in typescript first.


A. Packages made in Typescript

So, there are 3 simple steps that you need to follow to setup types correctly.


1. Generate Types from ts/tsx files

We are going to modify our typescript configuration (tsconfig.json) to tell the typeScript complier to generate .d.ts definition files from the type definitions found in .ts/.tsx files. Inline types get automatically exported whereas private types (i.e. the ones which are not exported) must be exported so that they are included in the generated d.ts type definition file(s). Your compiler will generally raise a warning when you’re using non-exported types.

So, update your tsconfig.json as shown below

// tsconfig.json
{
  "compilerOptions": {
    // ...
    "outDir": "./dist/",
    "strict": true,
    "declaration": true,
  }
}

Adding this single line will produce new d.ts definitions files in your output directory (in our case the output directory is ./dist/ as per tsconfig.json).
Of course this won’t happen just yet. You need to build your application/package (via your module bundler like Webpack or with tsc) to see these newly generated d.ts files in your output directory.

After building, the output folder should contain the package – ./dist/package.js, alongwith its type definitions – ./dist/package.d.ts

Note: If your tsconfig.json contains "allowJs": true, then your generated d.ts files might appear in a sub-folder like ./dist/src/package.d.ts


2. Copy custom types to the folder

Adding "declaration": true to your tsconfig.json only exports the types defined within .ts/.tsx files. But what if you had defined types yourself separately, say in a file named types.d.ts and then imported those types wherever needed ?

These custom types don’t get copied automatically to your output folder. We certainly need to include these too. There are 2 ways to do it.

  • The simplest way is to just convert your typescript definition file (types.d.ts) into a regular typescript file (types.ts) so that it gets picked up by the compiler
  • A better way is to manually copy the concerned typescript definition file(s) to your output directory. We will see how to do this below.

So copying files is pretty simple. You can use cp (shell), rsync, robocopy or a platform independent npm package like copyfiles

// install copyfiles as a dev dependency
npm install copyfiles -D

// Update package.json
"scripts": {
  "copy-typescript-definitions": "copyfiles -u 1 \"src/**/*.d.ts\" dist"
}

Running the above script will copy every d.ts file from your src directory into your output directory. You could make this script a part of your build process to ensure having copied the required files each time you build.


3. Add types to package.json

The last step is to add the path to the typescript definition files in your package.json. This is how other IDE’s come to know about your package’s types.

// package.json
"main": "dist/package.js",
"types": "dist/package.d.ts",
// ...

The official docs say that “types” field shown above is synonymous with “typings” field, but we found that VS Code does not detect the typings correctly when you use “typings” field thereby failing to provide intellisense. So we stick to using "types": "dist/package.d.ts".

That’s all that there is to it.


To verify that your types are linked correctly before you publish, run npm link in your package’s root. Then go to another folder/project and run npm link package-name to include your package (it’s like locally installing your package). Then create a .ts file and import your package by name (just like you would if you had installed it via npm). You should see type-hinting working in your editor. If this happens, you’re all set!

If you publish your package on NPM, you should see a small TS logo next to your package name (as seen below) indicating that your types are linked correctly.
types npm package

Okay, but how do we add types to a package written in javascript ? (like jquery). Let’s see how…


B. Packages made in Javascript

To make types available for packages that are in javascript, we follow 2 simple steps:


1. Write Typescript Definitions

Well this one is obvious. The first thing to do is to write the types linked to your package in a d.ts file (say, for example, index.d.ts contains all the typescript types for your package/library).


2. Publish your types to the @types organisation

We now want to publish our typescript definitions to the @types organisation. Packages under the @types organization get published via an automated tool. To have your typescript definitions published as a @types package, you must submit a pull request to DefinitelyTyped.

Here is an example of how the typescript definitions of Jquery look like. Once your Pull request gets merged, you will be able to install types with NPM.

# install types for your package
npm install --save @types/package-name

When you head to your package’s NPM page, you will see that your package has a small “DT” logo next to it as seen below. It confirms that your typescript definitions were linked successfully.

link javascript package to @types organisation

Now you can have type-hinting for your javascript package/library by just installing the necessary types. It works out of the box because by default all ”@types” packages are included in your compilation (Ref). However, if you have modified the typeRoots in your tsconfig.json, be sure to include your packages types there to make it work.

That’s it for now! Hope it helps 🙂


References

Leave a Reply