A Smarter Way To Type Your JavaScript TypeScript vs. JSOS Overview Typed Functions (Multimethods) Union Types Enums Interfaces (Structs) Parametric Interfaces (Structs) Classes (Structs+Multimethods)

A Smarter Way To Type Your JavaScript

JSOS's type system is quite different from how TypeScript does types. It does not require any compiler or additional build steps. That's because JSOS's type system works its magic at runtime.

TypeScript vs. JSOS

While TypeScript utilizes structural typing almost exclusively, JSOS's type system is mostly nominal.

To put this difference in simpler terms:

In TypeScript, two objects are considered equal when they have the same shape.

In JSOS, two objects are only considered equal when they have an internal reference to the same type or a subtype thereof.

A nominal type system is arguably far stricter than a structural type system. This leads to less bugs in your software. And checking whether two things have the same type is also trivial in a nominative type system, since all that needs to be compared is the internal type tag of the two things, whereas you have to check every property and values of an object in a structural type system.

You might think that a nominal type system runs counter to the extensibility JSOS promises with its multimethods. But that's actually not the case at all.

JSOS solves this seeming contradiction by having a hierarchy of abstract types (types that cannot be instantiated) and concrete types (that can be instantiated) as subtypes of these abstract types.

Overview

TypeScript JS+JSOS
structural typing nominal typing
compiler no compiler
TypeScript Equivalent JS+JSOS
typed functions multimethods
interfaces structs
typed classes structs + multimethods
union types union types
intersection types intersection types
generics parametric structs
JSOS Equivalent TypeScript
multimethods --
dependent types --

Typed Functions (Multimethods)

function add(a: number, b: number): number {
    return a + b;
}
const { add } = Generic({expect: JSNumber});

def(add, [JSNumber, JSNumber], (a, b) => a + b);

Union Types

type key = string | number;
type day = "Mo" | "Tu" | "We" | "Th" | "Fr" | "Sa" | "Su";
const Key = Union(JSString, JSNumber);

const Day = JSString.where(
    s => ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"].includes(s)
);

Enums

enum Day {
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday
}
const { Day } = Enum;
const { Monday,
        Tuesday,
        Wednesday,
        Thursday,
        Friday,
        Saturday,
        Sunday     } = Day;

Interfaces (Structs)

interface User {
    id: number;
    name: string;
}
const { User } = Struct({
    id: JSNumber,
    name: JSString
});

Parametric Interfaces (Structs)

interface Line<T extends Point> {
    start: T;
    end: T;
}

const line: Line<Point2D> = {
    start: new Point2D(5, 10),
    end: new Point2D(13, 18)
};
const { Line } = Struct(Point)(T => ({
    start: T,
    end: T
}));

const line = Line(Point2D).new({
    start: Point2D.new(5, 10),
    end: Point2D.new(13, 18)
});

Classes (Structs+Multimethods)

class Song {

    public name: string;
    public artist: string;
    public album: string;
    public year: number;

    constructor(name: string, artist: string, album: string, year: number) {
        this.name = name;
        this.artist = artist;
        this.album = album;
        this.year = year;
    }

    toString(): string {
        return `Song: '${ this.name }' by '${ this.artist }' ` +
            `(${ this.album }, ${ this.year })`;
    }
}

const blackwaterPark: Song = new Song(
    "Blackwater Park",
    "Opeth",
    "Blackwater Park",
    2001
);

console.log(blackwaterPark.toString());
const { Song } = MStruct({
    name: JSString,
    artist: JSString,
    album: JSString,
    year: JSNumber
});

def(
    Song.new, [JSString, JSString, JSString, JSNumber],
    (name, artist, album, year) => Song.new({name, artist, album, year})
);

def(stringify, [Song], s =>
    `Song: '${ s.name }' by '${ s.artist }' (${ s.album }, ${ s.year })`);

const blackwaterPark = Song.new(
    "Blackwater Park",
    "Opeth",
    "Blackwater Park",
    2001
);

console.log(stringify(blackwaterPark));