Which version of EcmaScript should I use in the TypeScript configuration

TypeScript allows to convert most of the ES next features to ES3, ES5, ES6, ES2016, ES2017. Of course, you can also target ES Next. But which version should you target?

Why you should use the highest possible version?

Using the highest version allows allows you to write shorter code, and use more readable features, such as async/await, for..of, spread, etc. It's not only shorter, but also easier to debug. For instance, async/await is rewritten by TypeScript to a state machine. So, the call stack is harder to understand, steping into the next statement is not easy because you have to step into the step machine functions. The source map can sometimes helps, but it doesn't solve all issues. You can also blackbox some scripts. For instance, you can blackbox tslib if you import TypeScript helpers.

So, if you can target ES Next, do it! But unfortunately this is not always possible. Let's see how to choose the right version!

Select your target JS runtime

First, you must know which runtime you want to support. Do you need to run you application in a web browser and which one, in NodeJS or in Electron. Depending of this choice, you know the JS flavor you can use. For instance, if you choose Electron, you know it uses Chromium version XXX so you know which functionalities are available. In you use NodeJS, it also use V8, the JS engine of Chromium. So, it easy to know which features are supported. For web browser, it's a little more complicated. You may want to support multiple browsers, and multiple versions of each browser.

You can check which features are supported by the web browsers and js runtime here: https://kangax.github.io/compat-table/es6. Tip: you can change the ES version on the top.

For instance, if you want to target IE11, you'll have to target ES5. If you want to support NodeJS, Edge or Chromium, ES6 is ok.

Once you know which version you want to use, update the tsconfig.json file to reflect your decision:

{
    "compilerOptions": {
        "target": "ES2016" // "ES3" (default), "ES5", "ES6"/"ES2015", "ES2016", "ES2017" or "ESNext".
    }
}

Which libraries to target?

Changing the target version also change the available libraries. For instance, if you target ES5, you cannot use Promise. But Promise is not a feature that must be implemented by the engine. You can use another library to replace them, such as bluebird. This means you can target ES5 and use Promise as long as you add them using an external library. It's the same for Array.include, and lots of functions.

TypeScript allows to specify which libraries are available. You can select them in the configuration file tsconfig.json:

{
    "compilerOptions": {
        "lib": [
            "ES5",
            "ES2015.Promise",
            "ES2016.Array.Include"
        ],
    }
}

You can find the list of available libraries in the TypeScript documentation.

BTW, you can read my previous post to dynamically import polyfills

Development vs Release configurations?

As I said in the introduction, using a higher version of EcmaScript may help debugging your application. So, it may not be a bad idea to have 2 configurations, one for the development and another one for the release. For instance, the first one can target ES next because you are debugging on a recent browser, while the second one can target ES5 because your customers may use an older browser.

TypeScript supports the configuration inheritance. So, you can create common tsconfig.json that contains all the settings, and a tsconfig.dev.json that inherits from tsconfig.json. You can build using tsc tsconfig.dev.json. You can read the documentation about configuration inheritance for more information.

Babel

If you are using webpack, gulp or any build tool, you may consider the Babel option. The idea is to configure TypeScript to target ES Next, and transpile to another version using Babel. Based on the previous link, Babel allows transpiling more things to a lower ES version than TypeScript. Using webpack, you can also automatically include polyfills with a plugin such as webpack-polyfill-injector.

Conclusion

Choose the configuration that make you the most productive and which will run on your target browsers / runtimes.

Get the name of a TypeScript class at runtime

in .NET, it's easy to get the class name of an object using obj.GetType().Name. In JavaScript, this doesn't work: typeof obj return "object" or something else, but never the name of the class. This doesn't mean you cannot get the name of a class in JS.

In ES6, you can use Function.name to get the name of a function (documentation).

function test() { }
console.log(test.name); // print "test"

Well, in JavaScript, a class is a function! So, you can get its name using the name property:

class Sample { }
console.log(Sample.name); // print "Sample"

For an instance of a class, you can use the constructor property to get the constructor function: obj.constructor. This way you can get the name of the class by getting the name of the constructor function:

const obj = new Sample();
console.log(obj.constructor.name); // print "Sample"

Note 1: If you minify your scripts, some functions/classes may be renamed. So, the name of the class won't be the original name (I mean the name from your original source file), but the name after minification. UglifyJS has an option to not mangle some names uglifyjs ... -m reserved=['$', 'Sample']

Note 2: This doesn't work if the class contains a method named name. In this case, the browser won't automatically add the name property, so you won't be able to get the name of the class. You can read the specification about this behavior.

Note 3: TypeScript doesn't show constructor in the auto-completion. However, this is totally supported and typed.