Javascript type systems - the surprising parts
Abhishek Yadav
for JsFoo-2018
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
Types in JS
Same shape -> same type
Yay !
Structural types
// Typescript
// Flow has similar behaviour
class A {
x: string
}
interface B {
x: string;
}
let b: B = new A(); // Cool !
The alternative is nominal types - stick to the declared names. Common in class based OOP languages
But Javascript too has classes now
Types in JS
Flow allow nominal types with classes.
o_O
Structural types
// Flow
class A {
x: string
}
class B {
x: string;
}
let b: B = new A(); // <- Error
The Rationale
"Imagine two component classes that both have render() methods, these components could still have totally different purposes, but in a structural type system they’d be considered exactly the same.”
3.
Sealed objects
Types in JS
Objects get sealed.
Sealed - not immutable
🤔
Sealed objects
// Typescript
// Flow behaves the same
interface A {
x: number;
y: string;
}
let a1: A = { x:1, y: "str" };
a1.z = true; // Error: Property z doesn't exist on A
Looks kind of obvious here -> we are accessing a property that doesn't exist
But are undeclared objects spared ?
Types in JS
No
Sealed objects
// Typescript
// Flow behaves the same
let b = { x:1, y: "str" };
b.z = true; // Same Error
b.x = 234; // Its not immutable
Undeclared objects also get a type inferred.
And that type is also sealed
4.
Type widening
Types in JS
Flow allows wider objects to be assigned to smaller ones
Wider - has more properties
Okkkkay ... but why ?
Type widening
// only in Flow
type A = { x: number }
let a:A = { x: 123, y: 'abc' } // <-- Okay
Well one is, you cant go wrong this way. The type system will make sure you use only the properties from the declared (narrower type)
And also, wider objects can be considered a sub-type of the narrower one.
Types in JS
But thats also not without problems
Type-widening and union types don't play nice together.
Type widening
// 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
5.
Type Variance
Type variance
Types in JS
Helps decide if two function types are compatible
Subtypes | Supertypes | |
---|---|---|
Covariance | Yes | No |
Contravariance | No | Yes |
Bivariance | Yes | Yes |
Types in JS
In sound type systems,
function arguments should be contravariant,
and return values should be covariant
In other words,
a type safe function
can accept supertypes
and can return subtypes
F = (Car => Car) => string
G1 = (Sedan => Sedan) => string x
G2 = (Sedan => Car) => string √
G3 = (Car => Sedan) => string x
G4 = (Car => Car) => string √
Type variance
Type variance
Types in JS
// TODO - Typescript example
Flow
Sound.
Arguments: contravariant
Return: covariant
Typescript:
Not sound
Argument: Bivariant
6.
The List.head
The List.head
Types in JS
function shamelesslyFine(): string {
let arr = [];
return arr[0]; // <- No error in Typescript.
// <- No error in Flow.
}
List.head should always have some kind of Maybe type
But failing this didn't seem pragmatic to the designers
Flow does not do this because it would be extremely inconvenient to use. You would be forced to refine the type of every value you get when accessing an array.
(emphasis mine)
javascript-type-systems
By Abhishek Yadav
javascript-type-systems
- 1,311