Friday, September 28, 2018

Named vs Fat Arrow Functions in TypeScript

When working with TypeScript, it's important to understand some of the key differences between named and fat arrow functions. Let's have a look at a basic class with one of each type of function defined, and also the resulting ES5 that TypeScript transpiles to for a better understanding.

Here is a sample TypeScript class with the two different types of functions.

class MyTsClass {

    private myValue: number = 0;

    myNamedFunction(): void {
        setTimeout(function () {
            console.log(`Inside myNamedFunction, this.myValue = ${this.myValue}`);
        }, 1000);
    }

    myFatArrowFunction = (someValue: number) => {
        this.myValue = someValue;
        console.log(`Inside myFatArrowFunction, this.myValue = ${this.myValue}`);
    }
}

const myClass = new MyTsClass();          
myClass.myFatArrowFunction(1);
myClass.myNamedFunction();

Here is the resulting transpiled ES5 JavaScript.

"use strict";
var MyTsClass = /** @class */ (function () {
    function MyTsClass() {
        var _this = this;
        this.myValue = 0;
        this.myFatArrowFunction = function (someValue) {
            _this.myValue = someValue;
            console.log("Inside myFatArrowFunction, this.myValue = " + _this.myValue);
        };
    }
    MyTsClass.prototype.myNamedFunction = function () {
        var _this = this;
        setTimeout(function () {
            console.log("Inside myNamedFunction, this.myValue = " + _this.myValue);
        }, 1000);
    };
    return MyTsClass;
}());
var myClass = new MyTsClass();
myClass.myFatArrowFunction(1);
myClass.myNamedFunction();

For the purpose of this post, we'll concentrate on (2) main aspects of the code
  1. Where the method is created on the object, and the resulting performance impact
  2. How the this keyword behaves
It's important to understand the differences because there is a performance impact. Notice how the named function on the class is created and attached on the object's prototype. In this manner, the method is stored in memory only one time, as objects created from the same constructor point to a single prototype object. This is the more memory efficient implementation.

Now inspect the fat arrow's function in the ES5 code. Because the method is declared and created in the object's constructor (instance member), it will be declared again per each new instance of this type of object created. This has a memory and performance overhead. As a note, this is the identical resulting behavior to explicitly creating methods within the class constructor in TypeScript.

At this point we might be thinking, "Well that's enough let's just use named functions which will be declared on the object's prototype and be done with it!" There is more than meets the eye and the fat arrow function is quite useful. One of the main advantages is that fat arrow functions lexically capture the context of this in TypeScript. This is critically important in functions that contain callbacks or where re-assignment of instance variables might occur. The former use case is one that we run into often with observable callbacks in say Angular components. You may have run into issues with the this keyword and noticed it wasn't the correct context. The issue is if not using the fat arrow syntax, the context of the this keyword will be undefined. If strict mode isn't enabled, the context of this will be of the window object. If you look in the ES5 tanspiled code, you'll notice this is captured from outside the function body allowing our context to be captured correctly.

var _this = this;

Let's look at the console output from the original code above. Notice in the setTimeout call the use of this. However in this instance it's not the proper context resulting in undefined when accessed. 


The fix is to update the callback to use the fat arrow syntax which will capture the correct context of this

myNamedFunction(): void {
  setTimeout(() => {
    console.log(`Inside myNamedFunction, this.myValue = ${this.myValue}`);
  }, 1000);
}

If we run the code again, we get the console output we expect.


In a TypeScript project with the default configuration of "noImplicitThis": true set in the tsconfig.json file, you'll actually be warned in the IDE of this scenario, " 'this' implicitly has type 'any' because it does not have a type annotation." This would be an indication you need to use the fat arrow function instead.


There are other use cases beyond the ones I'm mentioning here today in regards to the behavior of this with inheritance and calling the parent class as well as other differences. However these are two key areas that should be understood, as I see the usage flip-flop without intent, and developers should be aware of the performance and behavior differences and create the correct type where appropriate. They have separate purposes, so I'm not a fan of just one or the other. Using only fat arrow functions have their performance impact, but shouldn't be avoided all together as they are instrumental on capturing the correct context of this when needed. 

Tuesday, September 18, 2018

Visual Studio Code Snippet - Triple Slash Directive

Sticking with the theme of my last blog post, Running ES6 Modules Native in the Browser using TypeScript, this post looks at pure JavaScript/TypeScript projects (i.e. NodeJS), that might not be using a framework or module loader, yet still need the ability to reference dependencies from other files. We've been doing this for years using the Triple-Slash Directive like the following:

/// <reference path="./src/ts/myClass.ts" />

In Visual Studio Code I was looking for an extension, snippet, or shortcut to type out the above. Interestingly I came up with nothing. Maybe it's inside another extension I hadn't seen, but rather than look for a needle in a haystack, I decided to quickly hand-roll my own snippet. This is trivial to do in Visual Studio Code. Here is the snippet for a triple-slash directive, and it can be used with the following prefix name: "tripSlash"

{
  "Triple_Slash_Directive": {
      "prefix": "tripSlash",
      "scope": "javascript,typescript",
      "body": [
        "/// <reference path=\"${1:path}\" />",
      ],
      "description": "Triple Slash Directive used for declaring dependencies between files when no module loader is used"
  }
}