Publishing TypeScript packages on npm with types

So, been a while since I blogged. I may be a little rusty. A couple of days ago, I was trying to figure out how to enable Intellisense for VS Code for an npm package I had published. Until that point, I had only consumed local TypeScript packages, which meant the compiler knew where to pick up the typings. But this time, I was consuming my own package from npm. Took me a while to figure it out, but it's surprisingly easy. package.json supports an entry called "types" or "typings" that points to the location of the typings declaration of the package. Simple!

Here's a step-by-step procedure on how to publish a TypeScript package to the npm repository.

  • Initialize the npm repo (Duh!)
npm init  
  • Create a gulpfile that handles all the tasks, including incremental compilation of typescript files, using browserify to generate a browser bundle, generation of the typings declaration files, etc. Here's an example gulpfile:
var gulp = require('gulp');  
var browserify = require('browserify');  
var tsb = require('gulp-tsb');  
var watchify = require('watchify');  
var assign = require('lodash.assign');  
var gutil = require('gulp-util');  
var uglify = require('gulp-uglify');  
var notifier = require('node-notifier');  
var source = require('vinyl-source-stream');  
var buffer = require('vinyl-buffer');  
var sourcemaps = require('gulp-sourcemaps');

// create and keep compiler
var compilation = tsb.create({  
    target: 'es3',
    module: 'commonjs',
    declaration: true, // Need the declaration file. Without this, Intellisense won't function.
    lib: ["es2015", "es2015.promise", "dom", "es5"],
});

// add custom browserify options here
var customOpts = {  
    entries: ['./src/main.js'],
    debug: true
};
var opts = assign({}, watchify.args, customOpts);  
var b = watchify(browserify(opts));

// Set up src ts build task
gulp.task("srcCompileTS", function () {  
    return gulp.src('main.ts')
        .pipe(compilation()) // <- compilation
        .pipe(gulp.dest('./src/'));

});

b.on('update', bundle); // on any dep update, runs the bundler  
b.on('log', gutil.log); // output build logs to terminal

function bundle() {  
    return b.bundle()
        // log errors if they happen
        .on('error', gutil.log.bind(gutil, 'Browserify Error'))
        // source denotes the destination of the minified file. Reads the input from customOpts
        .pipe(source('./dist/main.min.js'))
        // optional, remove if you don't need to buffer file contents
        .pipe(buffer())
        // optional, remove if you dont want sourcemaps
        .pipe(sourcemaps.init({
            loadMaps: true
        })) // loads map from browserify file
        // Add transformation tasks to the pipeline here.
        .pipe(uglify())
        .pipe(sourcemaps.write('./')) // writes .map file
        .pipe(gulp.dest('./'));
}

gulp.task("srcCompileJS", ["srcCompileTS"], bundle);

gulp.task('notifySRCComplete', ['srcCompileJS'], function () {  
    notifier.notify({
        'title': 'Javascript',
        'message': 'SRC Compilation done!'
    });
});

// Set up watch task
gulp.task('default', ['srcCompileTS', 'srcCompileJS', 'notifySRCComplete'], function () {  
    // SRC files watch
    gulp.watch('main.ts', ['srcCompileTS', 'srcCompileJS', 'notifySRCComplete'], function () {
        // Run srcCompileTS
        console.log("Src TS Watch fired!");
    });
});

Assuming all the code exists in main.ts.

  • Set the following values in package.json
"main": "./src/main.js",
"types": "./src/main.d.ts",

"main" points to the point of entry for a node module, and "types" points to the typings declaration file.

  • Once the code is complete, the package can be pushed by setting its appropriate version in the "version" field of package.json with the following commands
npm login # If not logged in  already  
npm publish  

That's all there is! You should now be able to search for your package on npm, and you can use your package along with the associated typings declaration as well.