Menu Close

TypeScript: Interfaces vs Types

typescript types vs interfaces

Introduction

In TypeScript, both types and interfaces are type declarations or type constructs. They describe the shape and constraints of data. I want to explain the use cases and differences between TypeScript interfaces and types.

Interface Syntax

Interfaces in TypeScript can have properties defined, unlike languages like Java and C#.

interface Animal {
   kingdom: string;
   phylum: string;
   classification: string;
   order: string;
   family: string;
   species: string;
}

let lynx: Animal = {
    kingdom: "Animalia",
    phylum: "Chordata",
    classification: "Mammalia",
    order: "Carnivora",
    family: "Felidae",
    species: "L. lynx"
}

console.log(lynx.species); // prints: L. lynx
TypeScript

let lynx is an object that matches the interface.

Extending

// on top of previously defined Animal interface, 
// this is possible to extend it (this interface will be merged with the previous one)
interface Animal {
    name?: string; // ? is an optional property
    age?: number;
}

lynx.name = "Cat";
lynx.age = 5;

console.log(lynx.name); // prints: Cat
TypeScript

// another way of extending
interface Animal {
   species: string | null;
}

interface Pet extends Animal {
    name: string | null;
    age: number | null;
}
TypeScript

Interface as Class contract

class Lynx implements Animal {

    kingdom: string = "";
    phylum: string = "";
    classification: string = "";
    order: string = "";
    family: string = "";
    species: string = "";

    // public keyword auto declares class property
    constructor(public name: string, public age: number) {}
}

let lynx:Lynx = new Lynx("Bob", 4);
lynx.kingdom = "Animalia";

console.log(lynx.name) // prints: Bob
console.log(lynx.kingdom) // prints: Animalia
TypeScript

Another Example

Interface Animal is extended with the same name and later by Pet. The Lynx Class implements the Pet interface and has to implement both properties and functions (methods). The properties may be set with getters and setters.

interface Animal {
   species: string | null;
   kingdom: string | null;
}

interface Animal {
    move(): void;
}

interface Pet extends Animal {
    name: string | null;
    age: number | null;
}

class Lynx implements Pet {

    private _species: string | null = null;
    public kingdom: string | null = null;

    constructor(private _name:string | null = null, protected _age:number | null = null) {

    }

    get species(): string | null {
        return this._species;
    }

    set species(value: string) {
        this._species = value;
    }

    get name(): string | null {
        return this._name;
    }

    set name(value: string) {
        this._name = value;
    }

    get age(): number | null {
        return this._age;
    }

    set age(value: number) {
        this._age = value;
    }

    public toString(): string {
        return Object.entries(this)
        .filter(([_, value]) => value != null)
        .map(([key, value]) => `${key}=${value}`)
        .join(", ");
    }

    public move(): void {
        console.log(this.name+" is moving");
    }
}

let lynxOne:Lynx = new Lynx();
lynxOne.age = 3;
lynxOne.name = "Bobcat";
lynxOne.species = "L. lynx";
console.log(lynxOne.toString()); // prints: "_name=Bobcat, _age=3, _species=L. lynx"

let lynxTwo:Lynx = new Lynx("Ms. Kitty", 2);
console.log(lynxTwo.toString()); // prints: "_name=Ms. Kitty, _age=2" 
TypeScript

Type Syntax

type Animal = {
   kingdom?: string;
   phylum?: string;
   classification?: string;
   order?: string;
   family?: string;
   species: string;
}

let lynx: Animal = { 
    species: "L. lynx"
};

console.log(lynx.species);s // prints: L. lynx
TypeScript

Extending

type Animal = {
   kingdom?: string;
   phylum?: string;
   classification?: string;
   order?: string;
   family?: string;
   species: string;
}

// extending Animal with Pet properties
type Pet = Animal & { // & = intersection type
    name: string,
    age: number,
}

type Lynx = Animal | Pet; // | = union type

let lynx:Lynx = {
    species: "L. lynx"
}

console.log(lynx.species);
TypeScript

Here, when we define let lynx with a Lynx type, we can omit name and age, and it will be a valid Animal type. This is regarded as safe, because as soon when we try to access the properties name and age (from Pet), the TypeScript transpiler will complain about it and won’t let you.

  • Union type: a union type allows a value to be one of several types, like an OR relationship.
  • Intersection type: combinates multiple types into one, like an AND relationship.

Function Type

type Adder = (x: number, y: number) => number;
let add: Adder = (x, y) => x + y;
console.log(add(2,3));
TypeScript

Another example

type Animal = {
    species: string;
}

type Pet = Animal & {
    name: string;
    age: number;
}

type Moveable = {
    move(): void;
}

type Lynx = (Pet | Animal) & Moveable

let lynx_1: Lynx = {
    species: "L. lynx",
    move() {
        console.log(this.species+" is moving");
    }
}

lynx_1.move(); // prints: "L. lynx is moving" 
TypeScript

Lynx can either be Pet or Animal and is Moveable.

Types in Interfaces, Interfaces in Types

// Animal type
type Animal = {
    species: string;
    endangered?: boolean;
}

// Pet interface uses Animal type
interface Pet {
    animal: Animal; // type inside interface
    name: string;
}

// Instantiate Animal type
let cat:Animal = {
    species: "F. catus",
    endangered: false
}

// Instantiate Pet object with Animal type 
let bob:Pet = {
    animal: cat,
    name: "Bob",
}

// A Habitat type with a residen typed as an interface: Pet
type Habibat = {
    resident: Pet; // interface inside type
    location: string;
}

// Instantiate house habitat with bob (a Pet) inside
let houseHabitat = {
    resident: bob,
    location: "House"
}

// output: Bob is a F. catus
console.log(houseHabitat.resident.name+ " is a "+ houseHabitat.resident.animal.species);

// output: Bob lives in a House
console.log(houseHabitat.resident.name+ " lives in a "+ houseHabitat.location);

TypeScript

Conclusion

The TypeScript types and interfaces are unlike most other languages. Coming from Java/C# especially Type is somewhat alien, but its concepts are used in languages like F#, Haskell and Scala. Interfaces best describe object shapes with both variables and functions and tend to be best used in collaboration with classes.

Related Posts