Javascript type systems

The good parts

1

The List.head

The List.head problem

Types in JS

    
// Should this fail type-check

function foo(): string { // returns a string type
  let arr = [];
  return arr[0];
}

Types in JS

  • NO
  • Array access is ignored in type checks
  • Or else there will be far too many type errors

Developer ergonomics

The List.head problem

2.

The null

The null

Types in JS

    


let a:string = "a string"
a = null;   // <- No type-fail in Typescript
            // <- Fails with Flow

Types in JS

  • Typescript -
    • ​null and undefined are subtypes of all types
    • Can be assigned to a variable of any type
    • Reduces type safety drastically, and kind of defeats the purpose
    • But
    • Improves ease of introduction
    • And,
      • strictNullChecks that disables this behaviour
  • ​​Flow type
    • ​No such thing. Always strict
    • Add flexibility with Maybe types

The null

Onboarding experience

3.

Structural types

Structural types

Types in JS

// Typescript here.
// Flow has similar behaviour

class A{
    x: string
}

interface B {
    x: string;
}

type C = { x: string };

let a = new A();
let b: B;
let c: C;


b = a;  // works
c = a;  // works 

same shape => same type

Types in JS

  • Structural type-systems use the shape to determine type compatibility
  • The opposite is a nominal type system - where the declared name is the final decider
  • Nominal type systems are common in class based type languages - C#, Java
  • Javascript too has classes now

Structural types

Types in JS

Javascript too has classes now, so classes are nominally typed in Flow

Structural types

// only in Flow

class E {
  x: string;
}

class F {
  x: string;
}

let e1 = new E();
let f1:F = e1;   // <-- Fail

Types in JS

Flow goes a step further

Structural types

// only in Flow

type A = { x: number }

let a:A = { x: 123, y: 'abc' } // <-- Okay with Flow

Types in JS

Flow goes a step further -

It allows wider values to be assigned to an object

Structural types

// only in Flow

type A = { x: number, y: number }

let a:A = { x: 123, y: 456, z: 'abc' } // <-- Okay with Flow

let a2:A = { x: 123 } // Not okay

In fact, it allows an value of a subtype to be assigned.

Flow goes a step further -

It allows wider values to be assigned to an object

Types in JS

Structural sub-typing / Type widening

Allows an value of a subtype to be assigned.

 

Inherited classes are well known as subtypes.

Wider objects are actually sub-types of an object

 

Structural types

// Flow


class A { }

class B extends A { }

let a1:A = new B(); // <- Okay


// Same in Typescript too

Types in JS

But this is not without problems.

Type-widening and union types don't play nice together.

Structural types

// Flow

type A = { x: number }
type B = { y: number }
type C = A | B;

let c:C = { x: 1, y: 1 } // Okay
let d = c.x              // Error: x is missing in B
let e = c.y              // Error: y is missing in A






type A1 = {| x: number |}
type A2 = {| y: number |}
type C = A | B

let c:C = { x: 1, y: 2 } // Error right here

Fixed using the exact object type

4.

Covariance

Contravariance

Bivariance

Types in JS

  • Helps decides type compatibility of function types 
  • Flow:
    • Arguments:  contravariant
    • return values: covariant
  • Typescript:
    • Arguments: bivariant
    • return values: covariant
  • Flow is sound. Typescript is not

Type variance

Types in JS

Contravariance

Type-widening and union types don't play nice together.

Fixed using the exact object type

Type variance

Javascript type systems - the surprising parts

1.

The null type

The null

Types in JS

    


let a: string = "a string"
a = null;   // <- No type-fail in Typescript


function foo(x):string {
    return undefined  // <- No error. Not cool !
}

Typescript considers null  a subtype of all types.

In other words, null/undefined can be assigned to a variable of any type

 

Thankfully,

  • Flow doesn't have this problem
  • --strictNullChecks

 

The null

Types in JS

    


let a: string = "a string"
a = null;   // <- No type-fail in Typescript


function foo(x):string {
    return undefined  // <- No error. Not cool !
}

The rationale -

Ease of use.

Converting existing code bases gradually is much easier with this.

 

2.

Structural typing

javascript-type-systems-2

By Abhishek Yadav

javascript-type-systems-2

  • 873