Introduction to TypeScript: More Than Just Types


27 August 2015, by

In this post we’re going to take a look at the exciting extra features of TypeScript, and see what provides beyond just JavaScript with types.

This is the final post in our 4-part series on TypeScript, so do take a look at our previous posts on type inference, type annotations and type declarations for more background.

ES015 (aka ES6)

ES2015 has introduced a whole swathe of features, including arrow functions, classes, let/const and promises. Browser vendors are rapidly working to add these features, and so is TypeScript, in its ongoing effort to act as a superset of standard JavaScript syntax. Progress on this so far is good, with around 52% support for the new features added, compared to 48% for Chrome, 67% for Firefox and 66% for Edge. That 52% figure does require a browser that supports that ES6 syntax though (or a polyfill, like core-js), in addition to you compiling the TypeScript with the ‘--target ES6‘ option. Still, this means you can typically immediately start using ES6 features supported by your target browser set in TypeScript.

We can do better than this though. In lots of cases ES2015 features can be compiled back into ES5 or even ES3-compatible code, allowing you to use new ES2015 features right now, and run it even in substantially older browsers. This works to varying degrees, and is difficult to accurately measure since there’s a few specific cases that can’t be effectively worked around in special circumstances (like closuring a ‘let’ declaration within a loop), but overall TypeScript does currently support around 30% of the ES2015 features even when targeting ES5.

That means out of the box when writing TypeScript in an environment that doesn’t support ES2015, you can still immediately use:

  • Arrow functions:  (x) => x * 2
  • Destructuring:  var {a, b} = {a:1, b:2}
  • Spread operator:  function f(...x) { }; var xs = [1, 2, ...iterable, 5, 6];
  • For/of:  for (var x of [1, 2, 3]) { ... }
  • Let/const:  let x = 1; const y = "hi"
  • Template strings:  y = `hello ${myVar}`
  • Tagged template strings:  y = escapeHtml`<script>...</script>`
  • Classes (as seen in our previous post)
  • ES2015 modules (see ‘module systems’ below)
  • Unicode characters outside BMP:  "\u{1f4a9}"
  • Default parameters:  function f(a = 1) { }

And probably more, although there doesn’t seem to be an authoritative list anywhere. All of these get successfully compiled back into their non-ES2015 equivalents, allowing you to write enjoyably modern and clean ES2015 code without giving up on compatibility with older browsers. The TypeScript team are aiming to extend this further, wherever it’s possible to do so with reasonably simple and performant equivalents. All of the above fit that, and a few cases have no runtime impact at all: let/const are emitted just as ‘var’ statements for example, but checked for correct usage at compile time instead.

Paste any of above into the TypeScript Playground and take a look at the resulting compiled ES5 code that appears, if you want to see how this works in practice.

Extended class syntax

In addition to supporting the ES2015 class syntax, TypeScript also includes a few extensions. Some of these we’ve seen in previous posts, such as field visibility (public/private/protected), but there’s a couple of other interesting features.

Constructor parameter properties

class MyClass {
  constructor(private x: string) {}
}

In a syntax similar to that of Scala, TypeScript provides a shorthand to let you take a parameter in your constructor and immediately assign it to a public, private or protected field. The above code is exactly equivalent to (but shorter and simpler than):

class MyClass {
  private x: string;
  constructor(x: string) {
    this.x = x;
  }
}

Decorators

Taking a leaf from the current ES2016 (ES7) drafts, in turn heavily inspired by Python, TypeScript 1.5 includes support for class, method, property and parameter decorators. These are quite complicated and very new (feel free to read the ES7 proposed implementation docs for all the gory details), but provide some exciting new options for flexibility and DRY code when defining classes.

The usage of the 4 various decorator types looks like:

@myClassDecorator
class AClass {

  @myPropertyDecorator
  private x: string;
  
  @myMethodDecorator
  public f(@myParamDecorator x: number) { }  

}

In each case, the decorator wraps the decorated element, and potentially redefines or extends how it works.

You can for example create a @log method decorator that logs a message before and after the method is called, to give you a trace you can analyse to understand the flow in your application. Each form of decorator works in a slightly different way to allow this kind of wrapping; a class decorator is given the class’s constructor for example, and must return the new constructor that will replace it.

Digging into the depths of this is really out of the scope of this post, but decorators are a complicated and powerful tool for sharing cross-cutting logic between multiple classes that’s well worth learning to keep your code clean as your codebase expands. Take a look at this excellent TypeScript writeup if you’re keen to start looking into understanding and using these immediately (but I’d recommend getting to grips with the rest of TypeScript first).

Note again, as with many of the ES2015 features, that this is backward compatible and happily compiles down to standard ES5 code, so you can start writing code using it immediately.

Module systems

For a long time JavaScript has had an ongoing battle between various approaches to modularization, from globals to IIFE to CommonJS and AMD, to ES6 modules, and more, each introducing different syntax, functionality, and new sets of problems.

TypeScript lets you push straight past this. TypeScript includes it’s own standard built-in module system, sticking closely to the standard ES6 approach, and automatically hooked into the type system, so it can understand the type of the value that’s just been imported by looking at its corresponding source at compile time. The syntax for this is fairly simple and easy to understand if you’ve ever used either CommonJS or the new ES6 modules:

file1.ts:

export default function myFunction(): boolean {
  ...
  return true;
}

file2.ts:

import myFunction from "./file1";

var y: boolean = myFunction(); // understands types from the other file automatically

There’s more to it than this, but that’s the essence. The reason this is particularly intesting though, rather than just being another ES2015 feature, is that the output module format of TypeScript compilation is configurable. By specifying the ‘--module‘ argument, you can compile from this to code that supports any other module format supported. Currently that’s:

  • CommonJS
  • AMD
  • UMD
  • SystemJS
  • ES6 (by specifying ‘--target es6‘ and not specifying the ‘module’ argument)

This lets you write code in the most modern and powerful format available (ES2015 modules), while transparently compiling back to whatever format you need to use to integrate with the rest of your existing code and libraries.

Coming soon…

All this is already in TypeScript, but there’s more coming soon too! The full roadmap is available at github.com/Microsoft/TypeScript/wiki/Roadmap, and it’s worth being aware of some of the particular highlights:

  • Async/await – drawn from ES2016, inspired in turn by C#. Synchronous looking and feeling APIs for async code, to make asynchrony as frictionless as possible
  • Generators – another ES2015 feature, this one coming from Python. Syntax for describing functions that can repeatedly return values on demand, letting you treat them as iterables
  • JSX support – fluent syntax for creating HTML elements when using React

The End

This concludes our 4-part series on TypeScript. Hopefully it’s been an interesting insight into the language, how and why you might use it, and given you an itch to try it out! TypeScript is a exciting language, providing safety, structure, and a host of powerful features that can be used incrementally on top of the JavaScript you already writing, giving you remarkable flexibility to progressively pull modern development practices from a range of other languages into your JS.

If you are interested in having a closer look, the easiest way to quickly a go yourself is with the TypeScript playground. Setting it up in a real project isn’t much more complicated: you can build your code directly with the official command-line tool, or there’s plugins for grunt, gulp, broccoli, maven, gradle and probably whatever the latest flavour of the month build tool is. There’s also integrations abound in the tools you use for development, including Intellij, Visual Studio, Atom and Sublime Text.

Play around, try it out on your next project, and let us know your thoughts either at @SoftwireUK, or in the comments below.

Tags: , , , , ,

Categories: Technical

«
»

Leave a Reply

* Mandatory fields


2 × = sixteen

Submit Comment