Never
Any
Void
Null
Undefined
Unknown
Object
Function
Nil
None
Nada
Nil
Nada
None
Ok some are made up. Sort of.
We will be exploring the Typescript type system.
But the ideas will be generally applicable to other languages.
anyVar = stringVar;
neverVar = stringVar;
voidVar = unknownVar;
voidVar = undefinedVar;
neverVar = anyVar;
anyVar = neverVar;
stringVar = neverVar;
anyVar = unknownVar;
unknownVar = anyVar;What do you think?
let stringVar: string = 'string'
let fnVar: Function = () => {};
let objVar: Object = {};
let anyVar: any;
let undefinedVar: undefined;
let nullVar: null = null;
let voidVar: void;
let unknownVar: unknown;
let neverVar: never;Given these declarations
Can we do these?
What is inheritance?
A mechanism of extending one data type by another data type while retaining parts of the implementation
interface Animal {
live: boolean
};
interface Cat extends Animal {
meow: void
};
declare const cat: Cat;
cat.live // ok
cat.meow // ok
Any variable that is of type `Cat` is-an `Animal`
Not every `Animal` though is-a `Cat`
Inheritance is one mechanism that enables subtype polymorphism*
interface Animal {
live: boolean
};
interface Cat extends Animal {
meow: void
};
const animal: Animal = cat; // ok
const cat: Cat = animal; // not ok
// property 'meow' is missing in type 'animal'interface Supertype {};
interface Subtype extends Supertype {};
declare const subtype: Subtype;
const supertype: Supertype = subtype; // ok
const subtype: Subtype = supertype; // not okThe fancy way is to call it Liskov substitution principle
Mental model of a type system
Mental Model: It's actually a tree
Mental Model: It's actually a tree
declare const cat: Cat;
const feline: any = cat // ok because any is a top typeThe type at the top of the tree is called a Top Type
In our case, any is a top type
To understand never, consider this:
What should be the return of the following function?
function main() {
while (true) {};
}function error(message: string) {
throw new Error(message);
}The function error never actually returns a value
nor does this one
Never is an impossible type. There is no instance of never
Mental Model
Top type: anything can be assigned to it.
Bottom type: it can be assigned to anything.
Never is a bottom type. It can be assigned to anything, but nothing can be assigned to it.
const cat: Cat = neverVar; // ok
const neverVar: never = cat // error
// Cat not assignable to type never
Unknown - I don't know, should verify
Any - I don't care
Unknown, unlike Any, forces you to check the type
const notsure: unknown = "a string"; // (1)
notsure.toLowerCase(); // (2) Error: object is of type unknown
if (typeof notsure === "string") {
notsure.toLowerCase(); // ok
}How were we able to execute step #1?
never - cannot happen
Mental Model
unknown is also a top type
That means, anything can be assigned to unknown; Including any.
But since any is a top most type as well, any can be assigned to unknown.
Top type is a supertype of every other type
cat = animal; // not ok
stringVar = anyVar; // Definitely seen thatAny is both top AND bottom type
But not the most bottom type
Never is the bottomest type
anyVar = neverVar // ok
neverVar = anyVar // not okif undefined is a lack of value
Void is I don't know but it might be nothing
function foreach(ts, cb) {
for (const t of ts) {
cb(t);
}
}How should we type this? We don't care about the result of cb and foreach doesn't return anything
function foreach<T>(ts: T[], cb: (t: T) => any): any {
for (const t of ts) {
cb(t);
}
}Our return is unsafe
function foreach<T>(ts: T[], cb: (t: T) => any): any {
for (const t of ts) {
cb(t).boom(); // uh oh
}
}And so is our implementation
const apply = foreach([1, 2], (n) => console.log(n));
apply.boom(); // uh ohfunction foreach<T>(ts: T[], cb: (t: T) => undefined): undefined {
for (const t of ts) {
cb(t);
}
}That won't work either
const ts = [1,2,3];
const empty: number[] = [];
const cb = (t: number) => empty.push(t);
foreach(ts, cb) // Type 'number' is not assignable to type 'undefined'function foreach<T>(ts: T[], cb: (t: T) => unknown): unknown {
for (const t of ts) {
cb(t);
}
}
// A function whose declared type is neither 'void' nor 'any' must return a value.Here Typescript correctly points out you're not actually returning anything even though you promised.
We need a type to signify an unknown value that may just be nothing.
Unknown - I don't know, should check
Any - I don't care
Never - Can not happen
Void - Not sure, might be nothing
It's complicated
declare const cat: Cat;
const animal: Animal = cat; // okdeclare const cats: Array<Cat>;
const animals: Array<Animal> = cats; // maybeIs this ok?
If this is ok