Hitchiker's Guide to ECMASCript 2015/ES6 and TypeScript for the Angular2 beginner

by
Tags: , ,

Or, Babel without the fish, and so on…

Ok, I've piqued your interest: you want to learn about Angular, and you're prepared to start playing with TypeScript and ES6 today, and you want to get started. Completely understandable. Let's dive in together… To experiment, you'll need to load a recent version of Node.js – say the current 0.12 series.

Before TypeScript – ES6 today

Let's take some code from ES6 that provides a class definition (don't run away!) – in this sample, we're providing an employee constructor function (and methods on the prototype) using a class construct. To activate it on supported browsers, we have to place our function in strict mode. So:

(function() {
'use strict';
  class Employee {
    constructor(firstName, lastName, dob) {
      this.firstName = firstName;
      this.lastName = lastName;
      this.dob = dob;
    }
    getName() {
      return this.firstName +
             ' ' + this.lastName;
    }
  }
  var employee =
      new Employee('Ken', 'Kimple', new Date());
  console.log(employee.getName());
}());

Ok, that's just plain old ES6 / ES2015 code.

ES6 Classes – what are they?

In ES6 we build our constructors as classes (sidenote, see Exploring ES6, Classes Chapter for more details). So instead of writing functions with uppercase names, we use the class keyword, uppercase the class, and provide a single constructor method.

Ultimately this turns into a constructor function (even in ES6) but with the power to add functions and properties, and even provide super/subclassing support. Very powerful feature but in the end, it's just a smart constructor function.

Running that code in off-the-shelf Node.js won't work quite yet. But there are a few ways you can get it to work. You'd have to install a forked version of Node (io.js) that runs full ES6 and supports classes. But, since io.js is going to merge back in with Node.js eventually, that's just wasting your effort.

Executing server-side ES6 code with Babel

You could use Babel (pronounced Babble) – a transpiler that emits ES5 code from a set of ES6 files, to run Node.js in a souped-up mode and transpile the code on the fly to ES5:

npm install -g babel
babel-node employee.js

And, Tthis works, hooray! There are a lot of samples out there in the wild to play with. You can even use Code Pen, to experiment – just pull down your pen settings and select the Babel JavaScript Preprocessor.

But what about running ES6 in browsers today?

Transpiling to ES5 as part of a build

Finding fully-featured ES6 browsers is a real challenge – just try to find one! (I'll wait)… Back yet? Lots of red boxes, right? Ok. But don't give up! We can surely write code in ES6 today, but we have to send it down the pipeline with a transpiler to ES5 for any major browser to actually use it.

So, let's play with Babel as a true file-based transpiler rather than a node interpreter. To transpile our employee javascript file to ES5 syntax, we use:

babel employee.js > employee.es5.js

And now (for the most part) you have a working ES5 file that provides a constructor function and method provided to get the full name of the employee.

Tools to automate Babel

There are a number of ways to build Babel into your build process.

The most popular tool of late is Gulp, a Node.js-based streaming build tool that is super fast and super awesome (this humble programmer's opinion). There is an official plugin, gulp-babel whose homepage shows you how to integrate it with your build steps.

Also, you can install Babel as part of your Webpack or Browserify build steps, or even within your test runners, such as Karma. So there are a number of ways to go if you decide to code in ES6.

Angular and ES6

AngularJS will support ES6 developers out of the box. There is a specific modular distribution of angular for ES6 projects, and as long as you stick to the standards, you can code your application in ES6, and then transpile it down to ES5 and run the actual application in Angular for ES5.

But we're not completely done. We have to also shore up the browser you might be running, so that it has the features that ES6 added. For that, we also have a solution…

Shims, the way to cheat on browser standards…

For various ES6 features such as new methods on Array, String, etc, we need to provide so-called shims (which we'll discuss later) to add them. For an example of something that not every browser currentely implements, look at Array.prototype.find(). That's something that a shim like es6-shim can provide us. For a good list of ECMAScript shims, see this GitHub account.

There are some things Babel can't do, such as truly implement tail-recursive functions in ES5, because that requires the compiler to comply. But for the most part, this works.

Is ES6 the right path forward?

Yes, and no. You can certainly program Angular applications in ES6 and never go up to using TypeScript. But, because the Angular team worked on their APIs in TypeScript, you can also use it and reap both the rewards of ES6 and of the TypeSafe extensions. What are those, you ask?

Type safety

At the individual variable and collection level, TypeScript provides the ability to define data types. This is a huge advance, and allows you to squash a bunch of stupid bugs that normally would hit you at runtime.

To set up types for your variables, you add a colon and the type after each declaration, like this:

// individual variables
var foo: string = 'I am a string',
    data: array<Bar> = [];
// typed returns and parameters on functions
function foo(i: int): string {
  return "Hi I am " + i + " years old.";
}
// typed class members
class Employee {
  firstName: string;
  lastName: string;
  dob: Date;
  ...
}

This isn't just something you'd see as a warning in the browser. The typescript compiler, tsc, will actually watch this and refuse to compile a messy type expression. What's even better than that, you can use types to navigate your code in almost any modern editor including Atom, Sublime Text, Vim, WebStorm, and others.

Annotations

Angular TypeScript uses annotations (much like Java does) to decorate classes with instructions on how to function. For example, a visual component:

import { Component, View } from 'angular2/angular2';
@Component({
    selector: 'task-list'
})
@View({
    template: `
      <ul>
        <li>Magic</li>
        <li>Will</li>
        <li>Happen</li>
        <li>Here...</li>
      </ul>
    `
})
export class TaskList {
  tasks: Array<TaskModel> = [];
    constructor() {
        ...
    }
}

You may start to see how metadata can be easier to work with in annotations, rather than embedding it inside the code. We can quickly change the template of our component from static data to a set of dynamic directives, or even externalize it (as with Angular1) with templateUrl.

Oh, and by the way, goodbye to controllers and scopes. The way forward with Angular2 is via components. More about them in an upcoming post.

The TypeScript tooling

There is a lot happening at Microsoft this year around TypeScript. Their compiler, tsc, has undergone a lot of changes, much of it fed by the requests to add annotations to it by the Angular2 team.

To install the TypeScript compiler, do this:

npm install -g typescript@1.6.2

This installs the tsc compiler tool. You can use it by hand:

tsc myfile.ts
# produces myfile.js

or you can use a build tool like gulp-typescript which wraps it in the gulp build environment:

var ts = require('gulp-typescript');
var tsResult = gulp.src([...]))
                .pipe(ts())
                .dest('dist');
(or something like that. My example is more complex)

or, you can define a tsconfig.json file and give your editing tools (and the tsc compiler) a common configuration. This is absolutely fantastic for tools like Atom paired with atom-typescript:

# tsconfig.json
{
  "version": "1.6.0",
  "compilerOptions": {
    "rootDir": "src",
    "outDir": "web/js",
    "target": "es5",
    "module": "system",
    "declaration": false,
    "noImplicitAny": false,
    "removeComments": true,
    "noLib": false,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "sourceMap": true,
    "listFiles": true
  },
  "files": [
        "node_modules/angular2/bundles/typings/es6-shim/es6-shim.d.ts",
        "node_modules/angular2/bundles/typings/angular2/angular2.d.ts",
        "node_modules/angular2/bundles/typings/angular2/http.d.ts",
        "node_modules/angular2/bundles/typings/angular2/router.d.ts",
        "node_modules/rx/ts/rx.d.ts",
        "src/todo/app.ts",
        "src/todo/task-model.ts",
        "src/todo/task-list.ts",
        "src/todo/task-entry.ts"
  ]
}

Coming up next…

In our next post, we'll introduce Angular2 and my sample Angular2 seed project, and start hacking away on features together in Angular2 and Typescript.