Static Typing: Why Do It?

Static Typing: Why Do It? image

With the introduction of static typing to JavaScript through libraries like Flow.js and Typescript, some have found them totally unnecessary. Making the Angular2 docs default to TypeScript examples even caused more rage for people that wanted to use Angular and were not prepared to deal with a new technology or simply do not understand the benefits of static typing.

There is also a tendency that you just like them without knowing why you use them exactly so let us talk about that.

Code documentation

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.

~ Martin Fowler

While it is good to create a well structured and separate documentation for projects, you may also want to improve the developer experience (DX) for others working on the same code base and your future self with collocated documentation or what is known as self-documented code.

One of the many reasons why coding conventions suggest short functions is to allow a reader to see everything going on in the function at a viewport during inspection. The initial solution to collocated type documentation was usedoc which would look like this typically:

/** * @author chucknorris * @param {string} word - The word to truncate. * @param {number} length - The length of words to be truncated from * @returns {string} Truncated word */
function truncate(word, length){
  /* implement function */
}

In the example above the doc comments already occupies 5 sloc which may increase the possibility of our function extending beyond the screen. Hence making it harder to spot wrong code at a single view. The moment we scroll to view more code, the likely it is for us to lose context of some bits of the code.

The same code written with some type system would look like this:

function truncate(word: string, length: number): string {
  /* implement function */
}

There is not so much in the previous snippet with doc comments that differs from this one in terms of the documentation they offer. We have only made it more concise.

Additionally, this helps with the collocation coding convention.

Error detection

Static typing enhances error detection. In compiled languages, type errors happen at compile time which is a lot safer than having a run time error. JavaScript being a scripting language that opts into static typing through external libraries as flow and TS, compilation is rather abstracted, and the pre-runtime stage known to the developer is when the file gets saved. The popular type systems have extensions for IDEs and text editors that reveal type errors quick enough on file save. A common cause for unwanted type returns from functions is when multiple exit points are used:

function truncate(word, length){
  if(word.length > 2){
    return word.slice(0, 3);
  }
  return word;
}
truncate(489585, 3);

In the example above, another developer on the team might assume the truncate method works for both String and Number where the author had thought all word inputs would be strings only. The length property on an instance of Number would always return undefined. Moreover, undefined > 2 is False which would make the return value a Number.

Without a static type system, it might take longer to trace and debug such code. The term "type safety" is often used with static type checkers and hopefully, this example is convincing enough to prove the need for type safety.

Unit test reduction

Without a static type system, the way to address the problem above would be to assert that a string will always be returned with multiple datatypes as entry. Asserting for all primitive types would be so much work that you can do away with just by specifying types as shown.

function truncate(word: string, length: number): string{
  if(word.length > 2){
    return word.slice(0, 3);
  }
  return word;
}

This, however, does not mean unit tests should be forgone.

Performance optimization

Statically typed languages usually leverage ahead-of-time (AOT) compilation which happens before execution of code and greatly improves performance while most dynamically typed languages use dynamic translation aka just-in-time (JIT) compilation. With the amount of information provided to the compiler in a statically typed language, it can freely compile to machine code without waiting for execution. However, most JIT runtime have to translate to machine code at the same time of execution because the types cannot be predetermined.

V8 (JavaScript engine for Chrome and Node.js) tries to achieve utmost performance with its optimizing compiler. Techniques have been made to do great even with limited compile time type information but by having predictable and non-changing type we can help our programs perform even better.

Aiding Immutability

Static typing complements immutability. By having non-changing type, we are one step away from non-changing data. So far we have learned that letting a compiler know the type of data ahead of time can improve its performance. What if we could also assure it that a piece of data never changes? Then it does not have to bother with the new state of data at any point. If in any case, you are unaware of the benefits of immutable data, I would beeline a little deeper into it.

Consider the following snippet

const emojis = ['trollface', 'smirk', 'wink'];
const reactions = [];
const remarks = [];
const addToDom = (text, container) => {
  document.querySelector(container).textContent = text;
};

for(let i = 0; i < emojis.length; i++){
  const thisEmoji = emojis[i];
  reactions.push(`Hey you ${thisEmoji}`);
  remarks.push(`I really liked your product, ${thisEmoji}`);
}
addToDom(...reactions, '.reaction')
addToDom(...remarks, '.remark')

This is quite common in most JS code because 1. People still have not embraced functional style 2. We only think of a single thread of execution.

What if we could populate reaction and remarks on different threads. Assuming the emojis to load them with are more than just 3. We really do not want to wait for an iteration of 500 emojis then start pushing them to mutate empty arrays each time.

const emojis = ['trollface', 'smirk', 'wink'];
const addToDom = (text, container) => {
  document.querySelector(container).textContent = text;
};

new Thread(function(){
  const reactions = emojis.map(emoji => `Hey you ${emoji}`)
  addToDom(...reactions, '.reaction')
});

new Thread(function(){
  const remarks = emojis.map(emoji => `I really liked your product, ${emoji}`)
  addToDom(...remarks, '.remark')
});

This is a rather contrived example, but I hope it passes the message across.

Immutability aids thread safety, static types aid type safety and abstractly thread safety as well.

Prevents security vulnerabilities

A well-known article Making wrong code look wrong by Joel Spolsky explains how Systems Hungarian can be used to prevent an XSS vulnerability. The Apps Hungarian described in the same article is a precursor to static type systems in dynamically typed languages and when used with Systems Hungarian can produce really comprehensive code with security aiding conventions.

Everyone does it

This is no reason to do anything at all but let me enthrall you with some reasons you might want to use static typing based on this. Facebook made Flow.js and Microsoft made Typescript to introduce static typing to JavaScript.That says something right? Also, the renowned open sorcerer Sindre Sohrus recently tweeted this:

You should not use any technology because everyone else uses it, but as a software engineer, you are a scientist even if you never did sciences in college, and you should have some curiosity about why domain experts have chosen to use it, and give it a spin. If you do, try to also get some metrics, and you will see that there are more pros than cons to using static typing.

Summary

The goal of this post was not just to highlight the differences between static and dynamic type; that has been done enough times already. It is to convince people that think languages/systems like Flow.js and Typescript add extraneous complexity to code. I hope by now you can see that such systems are in fact meant to leave you with the opposite of a complex to comprehend code.


If the topic caught your fancy, you might want to check out our Introduction to TypeScript and dive into a typed superset of JavaScript directly.

KEEP MOVING FORWARD

Joseph Rex / code