Using latest JavaScript features in TypeScript, such as ES2018

The story is a bit more complex, and we should begin by separating it in two: language features and runtime features.

ES Language Features

When we say language features we mean changes to the core JavaScript language syntax. For example ES 2015 adds support for classes, arrow functions (=>), and for-of iteration

Typescript tries to implement all stable language features proposals as soon as possible and will down-compile them to the ES version specified as the target option to the compiler. So this means if you have the latest Typescript compiler, which adds support for a fresh new ES 2019 language feature, you will be able to down-compile it all the way down to ES3. Typescript will emit the code necessary to make such features work in whatever version of ES you are targeting.

And you can see this in action now. If you target ES5, arrow functions are compiled into regular functions and use a _this local variable to captures this. Classes are compiled to a function and the apropriate fields on the prototype set.

ES Runtime Features

In addition to the language features, we have certain runtime features that describe what built-in object types are available, and what methods and fields those runtime objects have. Examples of new object types in recent versions of ES would be Promise or Proxy.

Typescript does not provide poly-fills for such features, if the runtime does not offer support for these you will need to come with your own poly-fill implementation if you want to use them.

Typescript does however need to know what built-in objects exist at runtime and what their methods/fields are, this is where the lib option comes in. It allows you to specify what the runtime environment will look like.

So you could for example target es5, but specify that the runtime will have all the build-in objects in conformance with the es2015 standard (some might be implemented by the runtime itself, others may be added by you through poly-fills)

The intersection of the two

The division above is a simplification, in that some language features rely on the existence of certain built-in objects and methods.

For example, the async/await language feature relies on the existence of promises. So if you use async/await and target es5 you will get an error that the Promise constructor does not exist. If you target es5 but you specify lib: [ 'es2015', 'dom' ] you will no longer get an error as you have told the compiler that even though you wish to down compile to es5, at runtime the Promise constructor will exist as per the es2015 runtime specification represented in that particular lib(not the compiler’s problem how this will happen, poly-fills or built-in runtime behavior).

Generally if such a reliance exists, the typescript compiler will issue an error that certain types are missing and you can upgrade your lib, or change your target (which will change the default libs used), but you will have to ensure that the runtime has the necessary support.

The exceptions

It might not always be possible to down-compile language features all the way down to es3 (either because of missing runtime features, or just because of the high cost of implementing the feature does not make it a priority for the compiler team). An example would be property accessors (get/set) when targeting es3, which is unsupported. The compiler should warn you however if you are using an unsupported language feature/ target combination.

Leave a Comment