TypeScript is actually a polyfill. More than a language, TypeScript brings ES6 features to browsers (or better - JavaScript engines) that don't support support ES6 yet (or don't support a specific ES6 feature). TypeScript can compile to ES3 and ES5, but as ES5 is already almost a standard, ES3 is hardly used.
I was interested in the way TypeScript translates (better - transpiles) to JavaScript - how, for example, have they managed to bring the "let" keyword to ES5? Is it possible at all? I'll start with "let" and continue with all of the other wonderful TypeScript 1.5 features.
The "let" keyword
What are we trying to solve?
First, let's understand what "let" is supposed to do. Let's look at the following (JavaScript) code:
for(var i = 0; i < 100; ++i) {
//Do Something
}
console.log(i); //Outputs 100
Being a C++ or Java developer, you will look at this code and state that the final row will not output anything. Defining a variable in the for loop scope will invalidate it after the loop is finished. But since we are JavaScript developers, we know better. No block scope! It means that the variable i is being defined at the beginning of the function scope in which the for loop is defined. The variable i is defined way before the for loop starts. This can lead the a lof of confusion and misunderstandings.
Let to the rescue
Defining a variable using "let" will make sure it is only defined in the block scope in which it was defined. This is the result of using let in ES6:
for(let i = 0; i < 100; ++i) {
//Do Something
}
console.log(i); // ReferenceError: i is not defined
Let in TypeScript
Wonderful! Let was introduced in TypeScript 1.5.
As many ES6 features in TypeScript, most of them are simply compile time errors. They have no real solution in ES5. Let is simply not possible in ES5. So, this (typescript) code:
for(let i = 0; i < 100; ++i) {
//Do Something
}
console.log(i);
will show an error "Cannot find name 'i'." . It will, however, transpile to:
for(var i = 0; i < 100; ++i) {
//Do Something
}
console.log(i);
Which will output 100.
So, this is a compile-time-only feature. No actual implementation or a polyfill.
Destructuring in declarations and assignments
What are we solving?
Let's say we have a function, that returns this object:
function returnMinMax() {
return {min:0, max:100};
}
Using var minmax = returnMinmax()
we will get the object. We will then be able to use the values in the object. But if we want to have those values in specific variables, we will have to add:
var minmax = returnMinmax();
var min = minmax.min;
var max = minmax.max;
Destructuring let's us do that:
var {min, max} = returnMinmax();
Which is pretty neat. Both the min and the max variables will be defined afterwards, and we won't need to deal with the object at all.
Destructuring in TypeScript
Destructuring is more than a compile-time solution. It transpiles the above code to:
function returnMinMax() {
return { min: 0, max: 100 };
}
var _a = returnMinMax(), min = _a.min, max = _a.max;
The _a variable is a temporary variable. The typeScript compiler is doing that so it wouldn't need to execute the function for each variable.
There are some great things you could do with this feature. For example:
var {min, max, avg = (min+max/2)} = returnMinmax();
which translates to the (rather confusing) following code:
var _a = returnMinMax(), min = _a.min, max = _a.max, _b = _a.avg, avg = _b === void 0 ? (min + max / 2) : _b;
Compiling the example of value exchange of variables:
var x = 1;
var y = 2;
[x, y] = [y, x];
compiles to:
var x = 1;
var y = 2;
_c = [y, x], x = _c[0], y = _c[1];
var _c;
Which works. I find it funny that the var _c;
is defined at the end, thou it is perfectly "legal" to work this way.
The "const" keyword
What are we solving?
Many languages have a way of defining a variable as "unchangable". This is done to make sure, for example, that a function doesn't alter the referenced variable and only uses it. It is mainly there to be a warning for the developer - if a developer is changing a constant variable, the compiler should throw an error.
So this code (in ES6) should throw an exception:
const TMP = 1;
TMP = 3; //Impossible
const in TypeScript
Just like "let", const is not available in ES5 and can only be emulated by the compiler. When using "const", TypeScript uses "var".
So the code above will translate to:
var TMP = 1;
TMP = 3;
But will throw the following compile error: " Left-hand side of assignment expression cannot be a constant. ", which looks more like a C++ error than a JavaScript error. So cool :-)
Tagged Templates
What are we solving
The holy grail of strings-handling in JavaScript is String templates. They are here to solve the ugly hacks we need to introduce in our code in order to actually render a single string with variables inside. Template strings were introduced in TypeScript 1.4 already, but tagged templates support was only added in 1.5. To read more about tagged templates, read this wonderful article: http://odetocode.com/blogs/scott/archive/2014/09/30/features-of-es6-part-8-tagged-templates.aspx
Tagged Templates in TypeScript
The following TS code:
let upper = function (strings: TemplateStringsArray, ...values) {
let result = "";
for (let i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += values[i];
}
}
return result.toUpperCase();
};
var x = 1;
var y = 3;
var result = upper `${x} + ${y} is ${x + y}`;
is transpiled to:
var upper = function (strings) {
var values = [];
for (var _i = 1; _i < arguments.length; _i++) {
values[_i - 1] = arguments[_i];
}
var result = "";
for (var i = 0; i < strings.length; i++) {
result += strings[i];
if (i < values.length) {
result += values[i];
}
}
return result.toUpperCase();
};
var x = 1;
var y = 3;
var result = (_a = ["", " + ", " is ", ""], _a.raw = ["", " + ", " is ", ""], upper(_a, x, y, x + y));
var _a;
Which actually shows a few interesting features of TypeScript:
...values
as a function variable is compiled to:
var values = [];
for (var _i = 1; _i < arguments.length; _i++) {
values[_i - 1] = arguments[_i];
}
"let" is, as explained before, "ignored" during compilation, and the expression var result = upper `${x} + ${y} is ${x + y}`;
is compiled to: var result = (_a = ["", " + ", " is ", ""], _a.raw = ["", " + ", " is ", ""], upper(_a, x, y, x + y));
I wonder what the _a.raw
means and why it is even needed. If one of the readers do know, the comments is the right place for you!