What Type<T>[] are you?

Never

Any

 

Void

Null

Undefined

Unknown

Object

Function

Nil

None

Nada

 

Nil

Nada

 

None

Ok some are made up. Sort of.

Scala representation of emptiness

  • Nil
  • Null
  • Nothing
  • None
  • Unit

We will be exploring the Typescript type system.

But the ideas will be generally applicable to other languages.

Agenda

  • Pop Quiz
  • Purpose
  • What is inheritance?
  • Mental model of a type system

Pop Quiz

Can we do this?

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?

Purpose

What is inheritance?

What is inheritance

A mechanism of extending one data type by another data type while retaining parts of the implementation

What is inheritance: example

What is inheritance: more generically

What is inheritance?

interface Animal {
  live: boolean
};

interface Cat extends Animal {
  meow: void
};

declare const cat: Cat;

cat.live // ok
cat.meow // ok

What is inheritance?

Any variable that is of type `Cat` is-an `Animal`

What is inheritance?

Not every `Animal` though is-a `Cat`

What is inheritance?

Inheritance is one mechanism that enables subtype polymorphism*

What is 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'

In General

interface Supertype {};

interface Subtype extends Supertype {};

declare const subtype: Subtype;

const supertype: Supertype = subtype; // ok
const subtype: Subtype = supertype; // not ok

The fancy way is to call it Liskov substitution principle

Why is this relevant?

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 type

The type at the top of the tree is called a Top Type

In our case, any is a top type

What about the type never?

What is never?

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



What is the type unknown?

What is Unknown?

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

How do I assign Any to other values?

cat = animal; // not ok
stringVar = anyVar; // Definitely seen that

If Any is a top type

How do I assign Any?

Any is both top AND bottom type

But not the most bottom type

Never is the bottomest type

anyVar = neverVar // ok
neverVar = anyVar // not ok

So what is Void?

And how is it different from Undefined?

So what is Void?

if undefined is a lack of value

Void is I don't know but it might be nothing

So what is Void?

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 oh

So what is Void?

function 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'

So what is Void?

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.

So what is Void?

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; // ok
declare const cats: Array<Cat>;

const animals: Array<Animal> = cats; // maybe

Is this ok?

If this is ok

Are you my Type[]

By Leon Tager

Are you my Type[]

  • 382