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);
};