How To Use Babel And TypeScript, Together!

Use Case

The Selenium bindings for NodeJS are very nice that everything is a promise. When you are sending a command to the browser, this command might pass (say, return the element if found), or fail (say, trying to click an element that doesn’t exist), so, it’s a good fit for promises.

However, when I write tests, I think of them as a sequence of steps. With promises, the next step is always in a then() function.

But no one wants to keep nesting code like
x.then((y)=>y.then( (z)=>z.then(...) )).
Selenium extends promises so that you don’t have to do this all the time, but I soon hit limits (like loops).

Then I remembered reading about TypeScript support for async/await. When I tried it, the coding experience was nice, but I had to do a bit of setup.

Configuring Async / Await In TypeScript

I had to turn some experimental flags on, and target ES6. The easiest way to do this is to create a tsconfig.json file in the directory where I run tsc (TypeScript compiler) from. Here’s what mine mainly looked like:

    "compilerOptions": {
        "target": "ES6",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "experimentalAsyncFunctions": true
    "files": [
    "exclude": [

Node, ES6, Babel, And ES7

I tried to run the output of TypeScript in Node, but it didn’t work. It complained about import statements. It seems that Node doesn’t understand ES6 module system, not very surprising given it has its own module system too, commonJS.

TypeScript can target commonJS, but async/await is only supported when targeting ES6.

So, I decided to take the output through Babel before I run it with NodeJS.

Side Note: Babel already had support for async/await as it’s also coming to ES7. But by the time I got to that I was already happy with my autocomplete experience in TypeScript that I wanted to continue with this setup.

Using Atom Editor

It was so easy to get babel to work with TypeScript in Atom. I had atom-typescript package installed, and it suppoted an extra property in tsconfig.json:

<code>"externalTranspiler": "babel"

This is a NON-standard TypeScript property. But it allowed me to work very happily with Atom, and my .ts files processed via TypeScript and Babel whenever I save them.

This approach had a main no-go issue though. Anyone who pulls my code should be able to get it to work without me committing the “auto-generated” JS files, and without the others having my Atom setup, and building the project from Atom. This won’t work in CI build server.

Using Gulp Tasks

At this point, I decided to have a gulp task that does the transformation. It turned out to be very easy.

The following code sets gulp to process all .ts files in “app” folder via TypeScript and Babel, and save them as .js files:

<code>var gulp = require("gulp");
var ts = require("gulp-typescript");
var babel = require("gulp-babel");
var rename = require("gulp-rename");

gulp.task("ts-babel", function () {
    // Using my existing tsconfig.json file
    var tsProject = ts.createProject(__dirname + "/tsconfig.json");

    // The `base` part is needed so
    //  that `dest()` doesnt map folders correctly after rename
    return gulp.src("app/**/*.ts", { base: "./" })
            optional: ["runtime"]
        .pipe(rename(function (path) {
            path.extname = ".js";

You might wonder why did I do both transformations in the same task. That’s mainly to save disk writes, so that I don’t have to write an ES6 file that’s never going to be used except for Babel processing.

If you want to split them, should be easy, like:

<code>var gulp = require('gulp');
var ts = require('gulp-typescript');
var babel = require('gulp-babel');
var rename = require('gulp-rename');

gulp.task('tsc', function() {
    var tsProject = ts.createProject(__dirname + '/tsconfig.json');
    return gulp.src('app/**/*.ts', { base: './' })
        .pipe(rename(function (path) {
            path.extname = '.babel';

gulp.task('babel', function() {
    return gulp.src('app/**/*.babel', { base: './' })
        .pipe(rename(function (path) {
            path.extname = '.js';

gulp.task('ts-babel', ['tsc', 'babel']);

NPM Prerequisites

To run these, you need to install the needed packages:

<code>npm install gulp gulp-typescript gulp-babel gulp-rename babel-runtime --save-dev

Example & Conclusion

So, that’s all it takes to run the compilation/transpilation. You might want to hook this into nodemon or some gulp-watch task etc., but this is not specific to TypeScript or babel in any way, so, I thought it’s not worth mentioning here.

If you are curious about the code that I needed this setup for, check out my sample code post.

Share With Friends:

How did I learn that?

As a bonus for coming here, I'm giving away a free newsletter for web developers that you can sign up for from here.

It's not an anything-and-everything link list. It's thoughtfully collected picks of articles and tools, that focus on Angular 2+, ASP.NET (4.x/MVC5 and Core), and other fullstack developer goodies.

Take it for a test ride, and you may unsubscribe any time.

You might also want to support me by checking these out [Thanks]:

  • Benjamin Gruenbaum

    You’re using promises incorrectly – they chain.

    • Zorgatone

      Yep, that’s right. You wrote them wrong in the article :)

  • superjose

    Hiiiiii! One million thank you for the post. It helped a lot! One thing, apparently when using Babel 6+ one receives: “removed Babel 5 option: base.optional – Put the specific transforms you want in the `plugins` option”

    For solving that problem, I changed `optional: [“runtime”]` to presets: [“es2015”]. Voila! It solved the issue.

    Also, I’m targeting in the tsconfig.json file “target”: “es2015”! instead of “target”: “es5”,

    • Thanks!

      That was just an experiment. Nowadays I only use TypeScript by itself. It’s awesome you got this update!

      • superjose

        You’re welcome! I still do this, because Typescript does not play well when you destruct an array, like this: […Array(5).keys()] Babel does it wonderfully!

  • Nick Mitchell

    Great job, thank you for sharing your expertise. Been struggling with this. ;-)