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