ยท new features

What is Downleveling in TypeScript?

Downleveling is the process of converting modern TypeScript code into an older version of JavaScript. This allows developers to target older JavaScript environments that may not support the latest features.

Downleveling refers to the process of transpiling modern TypeScript code into an older version of JavaScript. It allows developers to target older JavaScript runtime environments that may not support the latest features and syntax introduced in newer versions of ECMAScript.

When downleveling is applied, TypeScript's compiler (tsc) converts your source code into an older version of JavaScript that is compatible with the specified target environment.

Contents

Downleveling Example

In ECMAScript 6 several new features were added like the for-of loop statement. When using an older version of JavaScript, such as ECMAScript 5, this syntax has to be downleveled in order to work.

Consider the following example:

Let's say you have written modern TypeScript code using the for...of loop:

const animals = ['cat', 'dog', 'zebra'];
 
for (const animal of animals) {
  console.log(animal);
}

If your "target" is set to "es6", the generated JavaScript code will adhere to the ES6 (ES2015) specification:

tsconfig.json
{
  "compilerOptions": {
    "target": "es6",
    "downlevelIteration": false,
    ...
}
'use strict';
const animals = ['cat', 'dog', 'zebra'];
for (const animal of animals) {
  console.log(animal);
}

Downleveling to ES5

If you set your "target" to an older version of JavaScript, such as ES5, the TypeScript compiler will downlevel your code and emit a traditional for loop instead of the modern for...of loop:

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "downlevelIteration": false,
    ...
}
'use strict';
var animals = ['cat', 'dog', 'zebra'];
for (var _i = 0, animals_1 = animals; _i < animals_1.length; _i++) {
  var animal = animals_1[_i];
  console.log(animal);
}

Please take note that the TypeScript compiler only performs downleveling of syntax (recognizable by special characters like =>, ?, `, # and keywords like class or static) but it does not downlevel the API itself. If you need to polyfill API calls, such as Array.prototype.flat introduced in ES2019, you would require an additional JavaScript compiler like Babel.

Progressive Enhancement with downlevelIteration

To progressively enhance your code for modern browsers while still targeting an ES5 environment, you can enable the downlevelIteration compiler option. This option generates a helper function that checks if modern Symbol.iterator property is present to support modern loops and iteration. If it's not, the emitted code will fallback to a legacy for loop:

tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "downlevelIteration": true,
    ...
}
'use strict';
var __values =
  (this && this.__values) ||
  function (o) {
    var s = typeof Symbol === 'function' && Symbol.iterator,
      m = s && o[s],
      i = 0;
    if (m) return m.call(o);
    if (o && typeof o.length === 'number')
      return {
        next: function () {
          if (o && i >= o.length) o = void 0;
          return { value: o && o[i++], done: !o };
        },
      };
    throw new TypeError(s ? 'Object is not iterable.' : 'Symbol.iterator is not defined.');
  };
var e_1, _a;
var animals = ['cat', 'dog', 'zebra'];
try {
  for (
    var animals_1 = __values(animals), animals_1_1 = animals_1.next();
    !animals_1_1.done;
    animals_1_1 = animals_1.next()
  ) {
    var animal = animals_1_1.value;
    console.log(animal);
  }
} catch (e_1_1) {
  e_1 = { error: e_1_1 };
} finally {
  try {
    if (animals_1_1 && !animals_1_1.done && (_a = animals_1.return)) _a.call(animals_1);
  } finally {
    if (e_1) throw e_1.error;
  }
}
Back to Blog