Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does an enum transpile into a function?

Consider:

enum Colors {
    Red,
    Green,
    Blue
}

It transpiles into this:

var Colors;
(function (Colors) {
    Colors[Colors["Red"] = 0] = "Red";
    Colors[Colors["Green"] = 1] = "Green";
    Colors[Colors["Blue"] = 2] = "Blue";
})(Colors || (Colors = {}));

Most questions about this result are answered in Enums in TypeScript: what is the JavaScript code doing?.

I quote the answers:

This is an 'immediately executing function'

And further down the topic:

think they could probably just go:

var Colors;
Colors || (Colors = {});
Colors[Colors["Cyan"] = 3] = "Cyan";
// ...

and skip the closure, but maybe I’m still missing something.

So the question remains: why wrapping this in an immediately executing function?

like image 941
Florimond Avatar asked Nov 18 '17 08:11

Florimond


Video Answer


2 Answers

I believe TypeScript uses IIFE (aka immediately-invoked function expression) because the variable name can be minified. If you have really long enum name, e.g. MySuperAmazingStatesNeededForWhateverIWantEnum, it can be minified inside an IIFE to just one letter

// amazing_enum.js
var MySuperAmazingStatesNeededForWhateverIWantEnum;
(function MySuperAmazingStatesNeededForWhateverIWantEnum() {
    MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum["Red"] = 0] = "Red";
    MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum["Green"] = 1] = "Green";
    MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum["Blue"] = 2] = "Blue";
})(MySuperAmazingStatesNeededForWhateverIWantEnum || (MySuperAmazingStatesNeededForWhateverIWantEnum = {}));

// amazing_enum.min.js
var MySuperAmazingStatesNeededForWhateverIWantEnum;!function e(){e[e.t=0]="Red",e[e.u=1]="Green",e[e.m=2]="Blue"}(MySuperAmazingStatesNeededForWhateverIWantEnum||(MySuperAmazingStatesNeededForWhateverIWantEnum={}));

Without IIFE minification wouldn't be as effective

// amazing_enum.js
var MySuperAmazingStatesNeededForWhateverIWantEnum;
MySuperAmazingStatesNeededForWhateverIWantEnum || (MySuperAmazingStatesNeededForWhateverIWantEnum = {}),
MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum.Red = 0] = "Red";
MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum.Green = 1] = "Green";
MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum.Blue = 2] = "Blue";

// amazing_enum.min.js
var MySuperAmazingStatesNeededForWhateverIWantEnum;MySuperAmazingStatesNeededForWhateverIWantEnum||(MySuperAmazingStatesNeededForWhateverIWantEnum={}),MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum.t=0]="Red",MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum.u=1]="Green",MySuperAmazingStatesNeededForWhateverIWantEnum[MySuperAmazingStatesNeededForWhateverIWantEnum.m=2]="Blue";

The difference in size is huge.

like image 77
An0num0us Avatar answered Oct 19 '22 22:10

An0num0us


That's because in TypeScript Enums are open ended.

This means that you can do something like below:

enum Colors {
 Red,
 Green,
 Blue
}

console.log(Colors); // { '0': 'Red', '1': 'Green', '2': 'Blue', Red: 0, Green: 1, Blue: 2 }

enum Colors {
 Black = 3, 
 White
}

console.log(Colors); // { '0': 'Red', '1': 'Green', '2': 'Blue', '3': 'Black', '4': 'White', Red: 0, Green: 1, Blue: 2, Black: 3, White: 4 }

That means that you can add new members to an already defined enum. Consider this as partial class in C# or interfaces in typescript.

The IIFE is used so that any existing definition (read object) of enum of the same name can be picked up.

Also TypeScript correctly shows reports the below error if we omit = 3 part from the second enum Colors definition.

In an enum with multiple declarations, only one declaration can omit an initializer for its first enum element.

(enum member) Colors.Black = 0

Read more here: https://basarat.gitbooks.io/typescript/docs/enums.html

EDIT: Also note that there are some benefit of using IIFE.

  1. Local scope optimization: It is always beneficial to declare your variable as close as possible to your first use of that variable. JavaScript can traverse from local scope to global scope in order to lookup a variable. Thus, using a local variable can provide performance benefit.
  2. Minification: this is already mentioned in the answer by @Piotr Kocia. It means that you can also use shorter variable names for the IIFE.

    var Colors;
    (function (C) {
        C[C["Red"] = 0] = "Red";
        C[C["Green"] = 1] = "Green";
        C[C["Blue"] = 2] = "Blue";
    })(Colors || (Colors = {}));
    

    This might not look a so lucrative proposition at this point but consider when you variable name is longer.

More on the topic "Why IIFE": http://gregfranko.com/blog/i-love-my-iife/

Hope this helps.

like image 44
Sayan Pal Avatar answered Oct 20 '22 00:10

Sayan Pal