Get : Typed

motivations for using types

edit

Catch simple mistakes early

Here is an untyped implementation of a function that draws a square on a canvas and returns its translation, rotation, and colour. By using the any type we have disabled Typescript’s static checks.

square0.ts
export const square0 : any = (size : any) : any => {
    size = Math.abs(size);
    return $gt.canvas(size + 100, ctx => {
        const x = $gt.randomInt(0, 50);
        const y = $gt.randomInt(0, 50);
        const r = $gt.randomFloat(0, 2 * Math.PI);
        const colour = $gt.randomInt(0, 0xffffff);
        ctx.translate(x + size, y + size);
        ctx.rotate(r);
        ctx.fillStyle = "#" + colour.toString(16);
        ctx.strokeStyle = "black";
        ctx.fillRect(-size / 2, -size / 2, size, size);
        ctx.strokeRect(-size / 2, -size / 2, size, size);
        return {
            colour,
            translation: [x, y],
            rotation: r
        };
    });
};

export const run = () => {
    const data = square0(10);
    $gt.log("square", data);
};

Here is the same function, typed.

square1.ts
import {square0} from "./square0";

type SquareInfo = {
    colour: number;
    translation: [number, number];
    rotation: number;
};

export const square1 = (size : number) : SquareInfo =>
    square0(size);

export const run = () => {
    const data = square1(10);
    $gt.log(data);
};

The rest of the page enumerates some mistakes that will manifest at runtime in an untyped program and at compile time in a typed program.

Some of the runtime programs fail silently and some throw errors. This is for variety. At worst case, each of these kinds of mistakes could result in a silent runtime deviation from expected behaviour.

Wrong arity

It could be that square0 originally took a colour argument, then someone inadvisedly changed it, and now this code silently ignores the argument.

wrong-arity0.ts
import {square0} from "./square0";

export const run = () => square0(25, "red");

With types, the error is caught at compile time.

wrong-arity1.ts
import {square1} from "./square1";

export const run = () => square1(25, "red");

Bad argument

Due to a mistake in documentation or a programmer’s memory, the function is called with a single named options argument. It silently fails to draw.

bad-argument0.ts
import {square0} from "./square0";

export const run = () => square0({size: 9});

With types, the error is caught at compile time.

bad-argument1.ts
import {square1} from "./square1";

export const run = () => square1({size: 9});

Mishandled return value

Trans-atlantic spelling differences lead to a typo in accessing a field on the return value.

mishandled-return-value0.ts
import {square0} from "./square0";

export const run = () => {
    const data = square0(12);
    const colorHex = data.color.toString(16);
    return colorHex;
};

With types, the error is caught at compile time.

mishandled-return-value1.ts
import {square1} from "./square1";

export const run = () => {
    const data = square1(12);
    const colorHex = data.color.toString(16);
    return colorHex;
};

Mishandled argument

A dependent function is written that contains a call to a non-existent method on the argument.

mishandled-argument0.ts
import {square0} from "./square0";

export const squares0 : any = (size : any) : any => {
    square0(size.sqrt());
    square0(size);
    square0(size * size);
};

export const run = () => {
    squares0(4);
};

With types, the error is caught at compile time.

mishandled-argument1.ts
import {square1} from "./square1";

export const squares1 = (size : number) : void => {
    square1(size.sqrt());
    // square1(Math.sqrt(size))
    square1(size);
    square1(size * size);
};

export const run = () => {
    squares1(4);
};