Update 11-Mar-2017: I put a little skeleton repo up at https://github.com/overthink/tstemplate

Each time I want to start a new project with TypeScript and npm I find that I’ve forgotten how to do it. This article is my personal reminder.


Approach

  • Prefer as little boilerplate as possible
  • Use aggressive TypeScript settings for typechecking
  • Don’t rely on anything installed globally (except npm)
    • i.e. npm install is the only command you need to get a get a freshly cloned repo working
  • fewer tools is better than more tools
    • i.e. npm (commands) for automation and dependencies (no grunt, no typings, no bower, etc)
  • webpack for bundling
  • As a working example, assume D3 and immutable.js as example external dependencies (i.e. loaded via <script> tags in the HTML, not bundled by webpack)
    • See “externals” section of webpack.config.js below for more

I don’t know if this is good or “cool”, but it has been working for me and seems relatively small and understandable.

How

mkdir myproj
cd myproj
mkdir src dist
npm init     # (use `./dist/bundle.js` as entry point)
npm install --save d3 immutable
npm install --save-dev typescript@2 webpack@2 ts-loader source-map-loader \
  webpack-dev-server@2 @types/d3 @types/immutable
echo "console.log('hello world');" > src/index.ts
git init

The @types stuff loads type definitions from DefinitelyTyped. There have been other ways to do this in the past, but this appears to be the TypeScript 2.x+ “best” way.

Files to add to project root

.gitignore

This will vary a lot by user. I use IDEA (commercial) and alm (free!) for TypeScript, and thus exclude their project files.

*.iml
.idea
.alm
dist
node_modules

.editorconfig

Optional. See http://editorconfig.org/

[*.{js,jsx,ts,tsx}]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = true
insert_final_newline = true

tsconfig.json

Create a tsconfig.json in the project root. This will vary a lot by project, but it’s a starting point.

{
  "compilerOptions": {
    "strictNullChecks": true,
    "noImplicitAny": true,
    "outDir": "./dist/",
    "sourceMap": true,
    "target": "es5"
  },
  "filesGlob": [
    "./src/*.ts"
  ]
}

webpack.config.js

Create webpack.config.js in the project root. Something like:

const path = require("path");
module.exports = {
    entry: "./src/index.ts",
    output: {
        filename: "./dist/bundle.js"
    },
    // Enable sourcemaps for debugging webpack's output.
    devtool: "source-map",
    resolve: {
        // Add '.ts' as resolvable extensions.
        extensions: [".ts", ".js"]
    },
    module: {
        rules: [
            {
                test: /\.js$/,
                use: ["source-map-loader"],
                enforce: "pre"
            },
            {
                test: /\.ts$/,
                use: ["ts-loader"]
            }
        ]
    },
    devServer: {
        contentBase: path.join(__dirname, "/dist"),
        compress: true,
        port: 8000
    },
    // Omit "externals" if you don't have any. Just an example because it's
    // common to have them.
    externals: [
        // Don't bundle giant dependencies, instead assume they're available in
        // the html doc as global variables node module name -> JS global
        // through which it is available
        {"d3": "d3",
         "immutable": "Immutable"}
    ]
};

package.json

Add npm script section

i.e. npm run commands for automation.

Add this to package.json. The only semi-interesting thing here is that I’m not relying on globally installed webpack.

  // add to package.json
  "scripts": {
    "start": "node_modules/.bin/webpack-dev-server",
    "build": "node_modules/.bin/webpack",
    "build:watch": "node_modules/.bin/webpack -w",
    "clean": "rm ./dist/*"
  },

References