Check out myAngular article series with live demos and Facebook group Angular - Advanced Topics

Closure Compiler and Types

Torgeir "Tor" Helgevold
- JavaScript Developer and Blogger
Published: Thu Feb 09 2017

In this post I will show to take advantage of the Closure Compiler's comprehensive typings annotations.

JavaScript is a weakly typed language. The lack of static typing can be an adjustment for developers coming from strongly typed languages like C# and C++.

To bridge the typings gap people have come up with ways to extend JavaScript to support typings.

Since the JavaScript language doesn't support strong typing natively, it's not possible to enforce typing rules at runtime. Instead we have to rely on compilers to do static type checking at compile time.

The theory behind this is that the compiler will be able to examine the code, and potentially prevent bugs if it detects type inconsistencies. One example might be passing a string to a function that expects a number.

In this post we will show how to use the Closure Compiler to add typings to JavaScript.

Another popular way to add typings to JavaScript is TypeScript. TypeScript is more than an extension of JavaScript, it's actually a whole different language. This is for many a barrier to entry since you now have to learn a new language.

The Closure compiler takes a different approach to typings. Instead of introducing a new language, types are added through JSDoc tags.

JSDoc tags are nothing more than JavaScript comments that follow a standard convention.

In the following examples I will show how to annotate regular JavaScript with type information that will be picked up by the Closure compiler.

Variables

In the first example I will show how to add types when declaring a variable.

/** @type {string} */ let firstName = 'Torgeir';

In the sample above I used a JSDoc tag called @type to declare a firstName string variable. If I try to assign the variable anything other than a string there will be a compilation error.

In addition to the type we can also declare the variable as a @const. This will prevent the variable from being assigned a value more than once.

/** @type {string} * @const */ let firstName = 'Torgeir';

Function Parameters

Next I will show how to add types to function arguments.

In the following sample I have created a function with two inputs of type number.

/** * @param {number} num1 * @param {number} num2 */ function addNumbers(num1, num2) { return num1 + num2; }

Trying to call the function with something that is not of type number will give a compilation error.

Constructor Functions

We can also declare constructor functions. The JSDoc tag for this is @constructor

/** * @constructor */ function Person() { /** * @type {string} */ this.firstName; /** * @type {string} */ this.lastName; /** * @type {number} */ this.age; } /** * @return {string} */ Person.prototype.getPersonalInfo = function() { return `${this.firstName} ${this.lastName}, age: ${this.age}`; }

Here I have also added a getPersonalInfo function with a return value of type string.

Classes

There is also support for annotations on classes:

class Car { constructor() { /** * @type {string} */ this.color; } /** * @return {string} */ getInfo() { return `The car is ${this.color}`; } }

Classes are similar to constructor functions, but we don't have to add the @constructor annotation.

Custom Types

So far I have been sticking to built-in types like number and string. It's also possible to use custom types.

In the next example I have declared an input to printPerson of type “Person”. This is the same “Person” type I declared above.

/** * @param {Person} person */ function printPerson(person) { console.log(person); }

Generics

The @template annotation is the Closure compiler's way to add support for generics.

Next I have created a generic Adder class. Generic means the class is configurable when it comes to type, instead of hardwired to assume a specific type.

/** * @template T */ class Adder { /** * @param val1 {T} * @param val2 {T} * @return {T} */ add(val1, val2) { return val1 + val2; } } /** @type {!Adder} */ let adderNumber = new Adder(); /** @type {!Adder} */ let adderString = new Adder();

In my example I create two instances of the class, one for numbers, and one for strings.

I have put the samples on Github in case you are interestd in checking them out.

If you liked this article, share it with your friends on social media:

We also have a new Facebook group about advanced Angular topics.

I invite you to follow me on twitter