Understanding TypeScript’s Type System

TypeScript is on the rise—and its type system is a big reason for that

Luke Mwila
May 31 · 6 min read
Github Octoverse 2018

Theory Of Types

Type systems find their origins in the theory of types developed by Bertrand Russell. In logic, type theory is essentially a system in which each term is given a type and operations are restricted based on types. Sounds familiar, doesn’t it?

x: nat
doubleUp: nat -> nat
var x: number;
doubleUp: (number) => number;

JavaScript vs TypeScript: Type Systems Compared

JavaScript type system

JavaScript is dynamically typed and therefore variables have no associated types. As mentioned above, you can assign a value of one type to a variable and later assign a value of a different type to the same variable.

// Assignment of different types
let date = '10/10/1991'; 
date = 10101991;

TypeScript type system

TypeScript’s system provides for specifying and inferring types, as well as setting types as optional. The optional setting allows you to choose when to enforce types and when to allow dynamic types. To opt out of type checking, you can use the any keyword.

// Assignment of different types
let date = '10/10/1991'; 
date = 10101991; // Results in a TypeScript compiler error

TypeScript’s Structural Type System

A type system is either structural or nominal, and in some cases it may have a mix of both within in a single type system (i.e. Flow).

class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }

let foo: Foo = new Bar(); // Works!
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: number) { /* ... */ } }

let foo: Foo = new Bar(); // Error!
class Foo { method(input: string) { /* ... */ } }
class Bar { method(input: string) { /* ... */ } }

let foo: Foo = new Bar(); // Error!

Type Erasure

Ever wondered what happens behind the scenes when the tsc command is run? You are obviously familiar with the part where TypeScript will compile and generate plain JavaScript code, but the newly generated code is different in the following ways:

TypeScript code

class OrderedArray<T> {
    private items: T[] = [];
    constructor(private comparer?: (a: T, b: T) => number) {
    }
    add(item: T): void {
        this.items.push(item);
        this.items.sort(this.comparer);
}
    getItem(index: number) : T {
        if (this.items.length > index) {
            return this.items[index];
        }
        return null;
    }
}
var orderedArray: OrderedArray<number> = new OrderedArray<number>();
orderedArray.add(5);
orderedArray.add(1);
orderedArray.add(3);
var firstItem: number = orderedArray.getItem(0);
alert(firstItem); // 1

Compiled JavaScript code

var OrderedArray = (function () {
    function OrderedArray(comparer) {
        this.comparer = comparer;
        this.items = [];
    }
    OrderedArray.prototype.add = function (item) {
        this.items.push(item);
        this.items.sort(this.comparer);
};OrderedArray.prototype.getItem = function (index) {
        if (this.items.length > index) {
            return this.items[index];
        }
        return null;
    };
    return OrderedArray;
})();
var orderedArray = new OrderedArray();
orderedArray.add(5);
orderedArray.add(1);
orderedArray.add(3);
var firstItem = orderedArray.getItem(0);
alert(firstItem); // 1

Type Inference

You may have guessed what this process entails because the name gives it away. One thing is for sure, it has nothing to do with what takes place in Type Erasure. Alright, so what is it? It’s the process by which types are determined at compile time in the absence of explicit type annotations.

Better Programming

Advice for programmers.

Luke Mwila

Written by

Software Engineer | Maker | Tech Enthusiast

Better Programming

Advice for programmers.