icon-search
static typing

Static typing in Angular 2

Jakub Majerz 13.10.2016

Angular 2 applications can be written both in Typescript and in plain old Javascript. This means we have an important choice to make: whether to go with static typing or choose the dynamically-typed path. At Sparkbit, we chose static typing and in this article we explain what it is and what the benefits of such approach are, when it comes to writing large-scale apps.

Dynamic vs Static typing

All right, so what’s the difference between statically- and dynamically-typed languages? Static typing (static type checking) requires that all variables and function return values be typed. In such languages, there is always a compile step and the compiler makes sure that we conform to these types. C++, Java and – you guessed it – Typescript are some examples of statically-typed languages. This is an example of a Typescript function:

public addTwoNumbers(num1: number, num2: number): number {
  return num1 + num2;
}

Here, we explicitly state that our addTwoNumbers() function takes two numeric values as arguments and that it returns a number. If we tried something like:

addTwoNumbers("one", "two")

The compiler wouldn’t let us do it. Moreover, suppose we have a method that expects one string parameter – functionExpectingString(). Then the compiler would complain (and inexorably stop us) if we tried calling it this way:

functionExpectingString(addTwoNumbers(3, 14));

Dynamically-typed languages, on the other hand, are not as restrictive and simply do not require explicit type declaration. So types are checked at runtime and (depending on the language itself) implicit type conversion may take place. Python and Javascript are examples of dynamically-typed languages. The above function would look this way in Javascript:

public addTwoNumbers(num1, num2) {
  return num1 + num2;
}

Without the help of the compiler, we are responsible for passing values that make sense. If we call it this way:

addTwoNumbers("3", 14)

We’ll get… 314. No error, no warning, nothing.

Benefits of static typing

The behavior described above may raise suspicions. Why is Javascript – the language of the Web – dynamically-typed then? Well, dynamic typing makes for faster development… as long as the project size is not too large. So as long as we simply want to enrich our website by adding some fancy animation or implement some basic logic, everything is fine. But if our goal is to develop a large-scale, complex application, hurdles start to arise. Let’s see how we can remedy this with static typing.

Development Support

First off, having type information, our IDE can aid us in development by providing many useful features. For example, if we mistakenly pass an argument of inapropriate type to a method, we’ll usually get some sort of visual hint and can correct the mistake right away. Since typing includes not only primitives such as numbers or strings but also custom objects, this can help us spot mistakes in method calls on our objects. Say we have a model class User that, among other things, contains a getFullName() method. If we mistakenly try to call a nonexistent getName() method on it, the IDE will let us know. Furthermore, it can also save us time by providing autocompletion mechanism where – having written initial part of the method name – we’ll get the available methods and we can have our IDE insert the full name for us. Neat! Even though Javascript IDEs have support for code autocompletion too, it can hardly be used outside of native Javascript methods and the most popular libraries. With Typescript we can enjoy this feature in each of our own code snippets. IDEs with support for Typescript include IntelliJ IDEA Ultimate, Brackets and Atom (the last two need plugins).

Example of Typescript autocompletion support in Brackets
Typescript autocompletion support in Brackets

Cleaner APIs

Static typing makes it easier to produce clean APIs in that the API consumer has full information on what the parameters are and what type the return value is. Say we have the following service:

@Injectable()
export class UserService {

  public getUser(userId: string): User {
    /* code that sends HTTP request to retrieve given user */
  }

  public getAllUsers(): Observable<User[]> {
    /* code that sends HTTP request to retrieve all users */
  }

}

Thanks to static typing any ambiguities regarding how to use these methods are eliminated. Without type annotations we may wonder whether getUser() expects an integer value or a string or maybe even an object that represents user id. Looking at method signatures, we also know exactly what to expect as return values. Without it we might have expected an array of Users as return value instead of an Observable of an array of Users in the getAllUsers() method.

Safer refactoring

Imagine that, for some reason, we wanted to refactor our UserService mentioned above. Maybe, for consistency’s sake, we wanted to change the return type of getAllUsers() from Observable<User[]> to User[]. Picture forgetting to change one consuming component‘s method using this service to factor in type change and watch the app blow up at runtime, leaving you wondering what and where went wrong. Maybe in this particular example it wouldn’t be that painful, but if you’ve dealt with Javascript – or any dynamically-typed language for that matter – long enough, odds are you’ve experienced wading throught the code only to discover that you made a typo somewhere. Choosing Typescript for our Angular 2 development makes us much more resistant to such errors (as the compiler will intervene). Furthermore, many IDEs have built-in support for refactoring statically-typed languages which makes the whole process faster and less error-prone.

Typescript code refactoring using IntelliJ IDEA Ultimate
Typescript code refactoring using IntelliJ IDEA Ultimate

Better dependency injection

Dependency Injection is a very useful pattern that can help make parts of the application loosely coupled. With static typing, this is really easy and clear in Angular 2. Injecting our UserService into a component is straightforward:

@Component({
  selector: "user-account"
  template: "./user-account.html"
})
export class UserAccountComponent {

  constructor(private userService: UserService /* <-- UserService is injected here */) {
    /* Constructor code */
  }

  /* Component code */

}

All we need to do is to include type annotation for a given constructor parameter and Angular’s Dependency Injection framework does the work for us.

Here’s an analogous snippet in Angular 1:

angular.module("sampleModule", [])
  .controller("UserAccountController", ["UserService", function(userService) {

    /* Controller code */

  }]);

Even though we use DI syntax that helps our code survive minification, this solution is still inferior because:

  • If there’s a typo in the service name, we won’t find out until runtime (the same goes for the service method names),
  • We can’t use our IDE’s type-awareness to facilitate development.

Lack of static typing in templates

As of this writing, static typing is – unfortunately – not fully available in Component templates. So if somewhere in our HTML we use a ProductsListComponent the following way:

<products-list [productsStream]="123"></products-list>

And ProductsListComponent requires productsStream input to be an Observable of objects of type Product, the compiler will not complain. But calling subscribe() on a numeric value will cause the app to explode at runtime. Thus, we need to be really careful about our types in HTML templates.

Angular team is working on an offline compilation mechanism that, among others, would make it possible to parse and typecheck HTML templates at build time. It’s still a work in progress but definitely worth waiting for. When such a mechanism is finally introduced, we’ll be able to fully enjoy the benefits of static typing in component templates as well.

Conclusion

Static typing can save lots of time (and nerves), as long as the project we work on is sufficiently large. Compiling acts as a shield against small yet frustrating-to-debug errors that can easily creep into a dynamically-typed environment such as Javascript. This is why we at Sparkbit have had no doubts about which version of Angular 2 to choose and can safely recommend that solution to anyone working on large-scale, complex applications.

comments: 0


Notice: Theme without comments.php is deprecated since version 3.0.0 with no alternative available. Please include a comments.php template in your theme. in /var/www/html/www_en/wp-includes/functions.php on line 4597

Leave a Reply

Your email address will not be published. Required fields are marked *