How to Write a TypeScript Library

September 2023

Writing modular code is a good idea. And nothing is more modular than writing a library. How can you write a TypeScript library? Well, that's exactly what this tutorial is about! This tutorial works with Typescript 5.x, TypeScript 4.x, TypeScript 3.x and TypeScript 2.x.

Step 1: Setup tsconfig.json

Create a project folder, in this tutorial we'll call it typescript-library. Then proceed to create a tsconfig.json in typescript-library. Your tsconfig.json file should look somewhat like this:

typescript-library/tsconfig.json
{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es2019",
    "declaration": true,
    "outDir": "./dist"
  },
  "include": [
    "src/**/*"
  ]
}

Pretty much like a setup for a non-library project, but with one important addition: You need to add the "declaration": true flag. This will generate the .d.ts files (aka declaration files) which contain the types of your code. This way, if someone is using your library and they also use TypeScript, they get the benefits of typesafety and autocomplete!

Regarding the other options, let's quickly go through those: The module compiler option "module": "commonjs" is required if you want your code to run seamlessly with most current node.js applications. Replace this with "module": "esnext" if you're building a library for the browser. "target": "es2015" specifies which version of JavaScript your code will get transpiled to. This needs to be aligned with the oldest version of node.js (or the oldest browser) you want to support. Choosing es2019 as the compile target makes your library compatible with node.js version 12 and upwards. In 2023, this is a good compromise of transpiling to a modern-ish javascript version (which in turn allows for better performance, more terse code, etc.) and still supporting older nodejs and browser versions. If you have a specific reason to go higher (more cutting-edge native JS features needed in the generated files) or lower (stronger backwards compatibility), you may of course change the version. Lastly, "outDir": "./dist" will write your compiled files into the dist folder and the include option specifies where your source code lives.

Step 2: Implement your library

Proceed in the same way, as if you weren't writing a library. Create a src folder and put all the source files of your library (application logic, data, and assets) there.

For this demo, we'll setup a silly hello-world.ts file, that looks like so:

typescript-library/src/hello-world.ts
export function sayHello() {
  console.log('hi')
}
export function sayGoodbye() {
  console.log('goodbye')
}

Step 3: Create an index.ts file

Add an index.ts file to your src folder. Its purpose is to export all the parts of the library you want to make available for consumers. In our case it would simply be:

typescript-library/src/index.ts
export {sayHello, sayGoodbye} from './hello-world'

The consumer would be able to use the library later on like so:

someotherproject/src/somefile.ts
import {sayHello} from 'hwrld'
sayHello();

You see that we have a new name here, "hwrld", we haven't seen anywhere yet. What is this name? It's the name of the library you're gonna publish to npm also known as the package name!

Step 4: Configure the package.json

The package name is what the consumer is going to use to import functionality from your library later on. For this demo I have have chosen hwrld since it was still available on npm. The package name is usually right at the top of the package.json. The whole package.json would look like so:

typescript-library/package.json
{
  "name": "hwrld",
  "version": "1.0.0",
  "description": "Can log \"hello world\" and \"goodbye world\" to the console!",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "files": [
    "/dist"
  ]
}

If you don't have a package yet you can create one with npm init and it will guide you through the process. Remember, the package name you choose will be used by people for their imports, so choose wisely!

There's also one all-important flag in this package.json: You have to declare where to find the type declarations! This is done using "types": "dist/index.d.ts" Otherwise the consumer won't find your module!

The final property files helps you to whitelist the files you want to ship to the npm registry. This is usually a much easier and safer route than using the .npmignore file!

Step 5: Publish to npm

To publish your first version to npm run:

tsc
npm publish

If you have a scoped package, for example @myscope/mypackage, then you need to run npm publish --access=public, since the default setting is private and for this you need a paid account.

Now you're all set to go! Consume your library anywhere you want by running:

npm install --save hwrld

and consume it using

import {sayHello} from 'hwrld'
sayHello();

For subsequent releases, use the semver principle. When you make a patch / bugfix to your library, you can run npm version patch, for new features run npm version minor and on breaking changes of your api run npm version major.

Check out the full source of the demo library on github: https://github.com/bersling/typescript-library-starter.

The above tutorial contains all the steps necessary to build & publish a working library. However, you should probably also include some unit tests and you might want to test the behavior of your library locally first, without publishing. Here are some more resources for this:

Dear Devs: You can help Ukraine🇺🇦. I opted for (a) this message and (b) a geo targeted message to Russians coming to this page. If you have a blog, you could do something similar, or you can link to a donations page. If you don't have one, you could think about launching a page with uncensored news and spread it on Russian forums or even Google Review. Or hack some russian servers. Get creative. #StandWithUkraine 🇺🇦
Dear russians🇷🇺. I am a peace loving person from Switzerland🇨🇭. It is without a doubt in my mind, that your president, Vladimir Putin, has started a war that causes death and suffering for Ukrainians🇺🇦 and Russians🇷🇺. Your media is full of lies. Lies about the casualties, about the intentions, about the "Nazi Regime" in Ukraine. Please help to mobilize your people against your leader. I know it's dangerous for you, but it must be done!