Skip to content

Interfaces ​

An interface describes the members something must have. It exists only for the checker and produces no code.

ts
interface Damageable {
    hp: int;
    takeDamage(amount: int): void;
}

class Crate implements Damageable {
    hp: int = 3;

    takeDamage(amount: int): void {
        this.hp -= amount;
    }
}

function smash(target: Damageable): void {
    target.takeDamage(1);
}

const crate: Crate = new Crate();
smash(crate);
console.log(crate.hp);

implements is checked ​

When a class says implements, the checker verifies that it really provides every member, with compatible types. A missing method is an error on the class name:

ts
class Ghost implements Damageable {
    hp: int = 1;
}
// error: Class 'Ghost' incorrectly implements interface 'Damageable': missing member 'takeDamage'

Members inherited from a parent class count.

Interfaces are nominal ​

This is the biggest difference from TypeScript. A class only counts as an interface when it says so. Having the same members is not enough:

ts
class Barrel {
    hp: int = 5;
    takeDamage(amount: int): void {}
}

const d: Damageable = new Barrel();   // error: Cannot assign Barrel to Damageable

Add implements Damageable to Barrel and the assignment is fine.

Optional and readonly members ​

ts
interface ItemInfo {
    readonly id: string;
    label: string;
    description?: string;
}

class Potion implements ItemInfo {
    readonly id: string = "potion";
    label: string = "Potion";
}

function describe(item: ItemInfo): string {
    return item.label + ": " + (item.description ?? "no description");
}

console.log(describe(new Potion()));
  • ? means a class may leave the member out. Reading it gives T | null, so handle the null with ??, ?. or a check.
  • readonly cannot be assigned through the interface.

Extending an interface ​

An interface can extend one other interface:

ts
interface Named {
    name: string;
}

interface Pet extends Named {
    owner: string;
}

class Cat implements Pet {
    name: string = "Mochi";
    owner: string = "Ada";
}

const n: Named = new Cat();
console.log(n.name);

A class can extend a parent and implement several interfaces: class GuideDog extends Dog implements Pet, Trained.

Generic interfaces ​

ts
interface Container<T> {
    get(): T;
    set(value: T): void;
}

class Slot implements Container<string> {
    private value: string = "";
    get(): string {
        return this.value;
    }
    set(value: string): void {
        this.value = value;
    }
}

const s: Slot = new Slot();
s.set("sword");
console.log(s.get());

Methods cannot be swapped ​

A method declared as go(): void; is a contract, not a field you can reassign. If you need a replaceable callback, declare a property with a function type instead: go: () => void;.