Embracing AtScript Typing

Steffen Schildknechtschrieb in Technologie, am
06.02.2015

Scaling development in dynamically typed languages such as JavaScript or Ruby up to the point that developers can no longer be aware of all parts of the application being developed presents a characteristic set of challenges. One of those challenges is assigning meaning to what the inputs and outputs of methods or functions represent - what kind of object is being passed around? What is its meaning and what can the program do with it? What is its type?

In compiled, statically typed languages such as Java or C++, a compiler, along with rigid syntactical structures, will take care of that. It will make sure that a method that is supposed to work with an object of a specific type will only ever receive that type of object. Dynamically typed languages often do not have that kind of luxury as they are missing a dedicated compile phase and present more challenges for static analysis to catch type errors. One of these languages is JavaScript.

Enter AtScript

AtScript is a small set of optional extensions to the ECMAScript 6 (ES6) language specification, which is the basis for current and up-and-coming JavaScript interpreters. AtScript is developed by the AngularJS team at Google and serves as the implementation language for the upcoming version 2.0 of the popular frontend framework. Despite its origin, AtScript’s extensions are framework agnostic and can be used in every kind of JavaScript application. The proposed extensions are as follows:

Type Annotations allow the definition of types and interfaces based upon those types to simplify reasoning about an application system.

Field Annotations are intended to emphasize the definition and structure of classes and types used in an application.

Metadata Annotations and Type Introspection allow adding additional meaning to parts of an application, which other parts of the application or a framework can reason about.

This post focuses on Type Annotations and Field Annotations to explore the possibilities of runtime type checking in JavaScript applications.

Getting Started with AtScript

AtScript implementation development is split between two projects:

The Traceur transpiler, a popular choice of transpiling ES6 code to ES5 targets, has been enhanced to optionally support AtScript’s language extensions.

Assert.js is an implementation of a runtime type checker which is called by transpiled AtScript code to define and compare types. Assert.js can be swapped with other implementations to support different kinds of type systems.

While the AngularJS team has provided an AtScript Playground repository, it has not been kept in sync with the current state of Traceur and Assert.js. You can find an updated version in our repository here, which also includes instructions to get up and running along with a set of specs for the different enhancements.

Basic Type Usage

The syntax for adding type information to valid ES6 code is derived from a proposed type system for ECMAScript 4. Simply putting a colon and a type after the thing you are annotating is enough:

The code above says that it expects the value being assigned to the variable aNumber to be aNumber. So what happens when we assign something different?

We get an exception - at runtime:

This mechanism is the basis for developing AtScript with type annotations - during development you will not have to backtrack through several layers of code to find out where things went wrong. You are informed immediately if a value does not fit the expectations - or contract - of your application.

You can do something similar when defining function parameters:

Or when defining a function’s return value:

By using type annotations we are essentially able to express a contract for a function. A developer can take a look at a function’s definition and reason about its inputs and outputs. The contract is enforced by checking inputs and outputs at runtime and generating exceptions if an expectation is not met. You can turn off these checks for production builds in order to not degrade performance.

Defining Types

There are a few built-in types in Assert.js: void, any, string, number, boolean.

void is mostly useful for checking that a function does not return anything.. any will literally match anything including undefined.

Using type annotations becomes increasingly interesting once we plug our own types into the system. If we decide to use an ES6 class as a type, the system will check whether a given parameter is an instance of that class.

This is nice, but it may prevent you from using type annotations in places where the expectations for an input are based upon its interface rather than upon its ancestry. E.g. you want to make sure that an input responds to a specific function call. This is where custom types can be of value:

The function passed to the assert.define call is evaluated to check whether the given argument adheres to the type Quackable.

When presenting AtScript at NgConf, it was also stressed that one might want to use structure type checking for validating Ajax Responses. This can be accomplished using a custom type and the assert.structure helper:

You may also want to use generic types. Generic Types allow you to express types that are containers for values of certain types:

Although the implementation of checking such types in Assert.js is not yet finished, we have implemented a version of it in a fork to showcase the feature.

Field Annotations

Field Annotations are a nice feature for expressing the type structure of a class. They allow us to use a shorthand for typed getters and setters of specific class fields:

So, should I use this?

AtScript type checking is a welcome extension to the ES6 language. While one could use the Assert.js library on itself to implement type checking manually, having type requirements directly available as a syntax feature should make type declarations effortless while programming and make programs easier to reason about when examining code. The added runtime checks could prove extremely useful while developing in big teams or with multiple teams.

Despite being a promising development and mostly usable, the AtScript type checking implementation is not finished yet. While Traceur seems to have most of the language features in place, Assert.js still needs work to support (more) generic types. So, while it is interesting to experiment with and maybe use for smaller projects we would advise you to wait for AtScript to stabilize until you use it in production code.