In this post I will show how to make external API responses compatible with Closure compiled code.

One of the optimizations of the Closure compiler is property mangling.

This is an effective way to shrink the footprint of an application, but it only works if the compiler can find, and update, every reference to the mangled property.

Example: If the original property name is mangled to a single character name, the compiler has to find all references to the original name and update accordingly.

This is often ok, but I run into an issue if the property name originated outside my own code. One typical example is a json response from an API.

The runtime value of the API response will not change even if the Closure compiler invented a new name for the property. The result is a runtime error when the application is trying to refer to the response from the API.

To prevent mangling of properties that originated outside the application, I can rely on a mechanism called “externs”.

Externs are just regular js files with a set of rules telling the Closure compiler to preserve the original name.

In the following Angular sample I will show how to wire this up.

import {Component} from '@angular/core'; import {Http} from '@angular/http'; export class CountryInfo { capitol: string; } @Component({ selector: 'country', template: 'Capitol: {{capitol}}' }) export class CountryComponent { capitol: string; constructor(private http: Http) {} ngOnInit() { this.http .get('country/usa') .map(res => res.json() as CountryInfo) .subscribe((res: CountryInfo) => { this.capitol = res.capitol; }); } }

Here I have a simple component making calls to a country API. You can see the returned API response below.

{ "capitol":"Washington DC" }

To get typing I am casting the response to a matching CountryInfo object.

Everything looks pretty standard, but when I run the application, I don't see the expected value in the view!

This is because the Closure compiler mangled my reference to “.capitol” and broke the contract with the returned json response.

How can I fix it?

All I have to do is create an extern that protects the capitol property.

I have defined my extern file below:

/** @externs */ /** * @type {string} */ CountryInfo.prototype.capitol;

In the extern file I have told the Closure compiler to not touch the “capitol” property on the CountryInfo type.

In addition to the property, it's important to annotate the file with the jsdoc @extern tag. Optionally I am also giving the property a @type annotation.

Currently these externs files have to be created manually, but it might one day be integrated with Angular AOT compilation.

One proposal I've heard is to automatically generate externs for objects implementing "declared" interfaces like the example below:

export declare interface CountryInfo { capitol: string; }