Table of Contents
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. lynxTypeScriptlet 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: CatTypeScript// another way of extending
interface Animal {
species: string | null;
}
interface Pet extends Animal {
name: string | null;
age: number | null;
}TypeScriptInterface 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: AnimaliaTypeScriptAnother 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" TypeScriptType 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
TypeScriptExtending
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);TypeScriptHere, 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));TypeScriptAnother 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" TypeScriptLynx 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);
TypeScriptConclusion
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.