Creating JavaScript Libraries with TypeScript and WebPack

I’ve been on a tear recently learning React, Redux and other pieces for developing a nice UI-driven application. I needed to switch attention this week. I wanted to write a JavaScript library. The library had to have several features before I even got to the code.

  • Written in TypeScript
  • Released as an ES5.1 UMD library
  • Usable as a named define, AMD and CommonJS library

I like using Webpack – it takes care of a lot of packaging for me. First off, let me introduce my basic library – and it is very basic. I have already done the following:

git init
npm init --yes

I’ve also altered the package.json file so that it has all my relevant bits in it. I’ve create a src directory with two TypeScript classes in it. The first one is called TestClass.ts and it will be exposed as a library entry-point. The second one is called SubClass.ts and it will not be exposed as part of the library. This allows me to test private classes. You can find the src directory on my GitHub repository.

On to the actual mechanics of the code. Let’s start by looking at my TypeScript config files – I have two. The first is the tsconfig.json file:

  "compileOnSave": false,
  "compilerOptions": {
    "declaration": false,
    "jsx": "react",
    "module": "commonjs",
    "noImplicitAny": true,
    "preserveConstEnums": true,
    "removeComments": false,
    "sourceMap": true,
    "target": "es5"
  "exclude": [

Most of the options are fairly common in TypeScript libraries. The important settings (and why):

  • “target” must be ES5 – my target language
  • “compileOnSave” is set to false to prevent the atom-typescript plugin from compiling on the fly
  • “sourceMap” is set to true so that I can integrate source-maps into the library
  • “declarations” is set to not emit the .d.ts files

Why don’t we emit the .d.ts files? Because one file is generated per source file. Generally you want one file per library so that it is easy to load in a browser context. As a result, I’ll deal with the .d.ts file later on – probably as part of a gulp workflow.

Pretty much everything else is up to you. I highly recommend looking over the tsconfig.json file docs and the TypeScript compiler options for all options.

My other file is tslint.json – I’m going to use tslint to check my files prior to compilation, but I like some changes to the linting rules.

  "rules": {
    "quotemark": [ true, "single" ]

I like to use single-quotes instead of double-quotes in my code. There is a big list of tslint rules that you can change – you should really understand them and make a reasonable decision based on your coding style.

On to Webpack. For webpack, we need to install some loaders:

npm install --save-dev webpack ts-loader tslint-loader typescript tslint

One thing to note – use ts-loader, not typescript-loader. Typescript-loader has not been updated for a while and is out of date with respect to its peer dependency – typescript. I want to use the latest version of typescript and ts-loader seems to keep up to date with the latest changes in typescript.

Now, onto the webpack.config.js:

var webpack = require('webpack'),
    path = require('path'),
    yargs = require('yargs');

var libraryName = 'MyLib',
    plugins = [],

if (yargs.argv.p) {
  plugins.push(new webpack.optimize.UglifyJsPlugin({ minimize: true }));
  outputFile = libraryName + '.min.js';
} else {
  outputFile = libraryName + '.js';

var config = {
  entry: [
    __dirname + '/src/TestClass.ts'
  devtool: 'source-map',
  output: {
    path: path.join(__dirname, '/dist'),
    filename: outputFile,
    library: libraryName,
    libraryTarget: 'umd',
    umdNamedDefine: true
  module: {
    preLoaders: [
      { test: /\.tsx?$/, loader: 'tslint', exclude: /node_modules/ }
    loaders: [
      { test: /\.tsx?$/, loader: 'ts', exclude: /node_modules/ }
  resolve: {
    root: path.resolve('./src'),
    extensions: [ '', '.js', '.ts', '.jsx', '.tsx' ]
  plugins: plugins,

  // Individual Plugin Options
  tslint: {
    emitErrors: true,
    failOnHint: true

module.exports = config;

There are, as is usual with webpack configurations, two areas to take a look at. The output section is the bit that emits this as a library. Note that I make the library property the name of the library – this is also the name of the global defined object containing the library. In this case, I expect to be able to use MyLib.TestClass in a browser when it is loaded. The other area is the module section. This contains a preLoader for running lint and a standard loader for compiling the TypeScript.

Note that I’ve created a block that determines whether -p was used on webpack – if it was, I generate the optimized version of the library. If not, then I generate the debug version of the library. There are several ways to accomplish this; I just picked one I knew would work.

My eventual library will contain React components, so I’ve set things up to compile JSX embedded in TypeScript. This includes setting the jsx flag in the tsconfig.json and adding the match for .tsx in the module section of the webpack.config.js file.

My final section is in the package.json file:

  "name": "example-typescript-webpack",
  "version": "0.1.0",
  "description": "An example Webpack deployment for using TypeScript as a library",
  "main": "dist/MyLib.min.js",
  "scripts": {
    "prepublish": "webpack --debug; webpack -p"
  "keywords": [
  "author": "Adrian Hall <>",
  "license": "MIT",
  "devDependencies": {
    "ts-loader": "^0.8.1",
    "tslint": "^3.5.0",
    "tslint-loader": "^2.1.0",
    "typescript": "^1.8.2",
    "webpack": "^1.12.14",
    "yargs": "^4.2.0"

Line 5 is important – it points to my production library and allows a developer to import the library into a CommonJS or AMD scenario. The scripts section is also interesting – the prepublish script (which is run with npm run prepublish) will generate both versions of the library into the dist directory. I’m using prepublish because I don’t need to check in the dist directory when publishing. When I run prepublish, the npm system will generate the right files for me and then publish those. Also, when running npm install locally, the same thing happens.

The net effect of that is that I can exclude the dist directory from my GitHub repository and still release a solid ES5 library.

Finally, let’s talk about testing. I want to do some preliminary testing against the library. I can test the CommonJS version easily enough with mocha and chai – here is my test/commonjs.tests.js file for that:

var expect = require('chai').expect;
var mylib = require('../dist/MyLib.min.js');

describe('TestClass', function () {
  it('is contained within MyLib as CommonJS', function () {

  it('can be instantiated', function () {
    var t = new mylib.TestClass('foo');

I’ve also written in the past about running tests in the browser, so I can do the AMD and Global versions there. In short, there is absolutely no reason to not test all three versions of the library before releasing.

You can find my template library on my GitHub Repository.