Interfaces in TypeScript

Learn how to use Interfaces in TypeScript with this detailed tutorial. Includes basic and advanced examples to help you master Interfaces.

Introduction

Interfaces in TypeScript allow you to define the structure of an object. They are a powerful way to enforce type checking and improve the readability and maintainability of your code. Interfaces can describe the shapes of objects, functions, arrays, and classes.

Basic Examples

Here are some basic examples of using Interfaces in TypeScript:

Defining an Interface

You can define an interface to describe the shape of an object.

interface User {
    id: number;
    name: string;
    email: string;
}

let user: User = {
    id: 1,
    name: "John Doe",
    email: "john.doe@example.com"
};

Optional Properties

You can define optional properties in an interface using the ? symbol.

interface User {
    id: number;
    name: string;
    email?: string; // email is optional
}

let user1: User = {
    id: 1,
    name: "John Doe"
};

let user2: User = {
    id: 2,
    name: "Jane Doe",
    email: "jane.doe@example.com"
};

Readonly Properties

You can define readonly properties in an interface using the readonly modifier.

interface User {
    readonly id: number;
    name: string;
}

let user: User = {
    id: 1,
    name: "John Doe"
};

// user.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.

Advanced Examples

Here are some advanced examples of using Interfaces in TypeScript:

Function Types

Interfaces can be used to define the type of a function.

interface SearchFunc {
    (source: string, subString: string): boolean;
}

let mySearch: SearchFunc;

mySearch = function(source: string, subString: string) {
    return source.search(subString) > -1;
}

Indexable Types

Interfaces can describe types that can be indexed like arrays or objects.

interface StringArray {
    [index: number]: string;
}

let myArray: StringArray;
myArray = ["Bob", "Fred"];

let myStr: string = myArray[0];

Class Types

Interfaces can be used to enforce the shape of classes.

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date): void;
}

class Clock implements ClockInterface {
    currentTime: Date = new Date();

    setTime(d: Date) {
        this.currentTime = d;
    }

    constructor(h: number, m: number) {}
}

Extending Interfaces

Interfaces can extend other interfaces, allowing you to combine multiple interfaces into one.

interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

let square: Square = {
    color: "blue",
    sideLength: 10
};

Hybrid Types

Interfaces can describe objects that act as both a function and an object.

interface Counter {
    (start: number): string;
    interval: number;
    reset(): void;
}

function getCounter(): Counter {
    let counter = function(start: number) {};
    counter.interval = 123;
    counter.reset = function() {};
    return counter;
}

let c = getCounter();
c(10);
c.reset();
c.interval = 5.0;

Interfaces with Arrays

Interfaces can be used to define the type of arrays.

interface StringArray {
    [index: number]: string;
}

let myArray: StringArray;
myArray = ["Alice", "Bob", "Charlie"];

console.log(myArray[0]); // Alice
console.log(myArray[1]); // Bob

Interfaces with Readonly Arrays

You can define interfaces for readonly arrays.

interface ReadonlyStringArray {
    readonly [index: number]: string;
}

let myArray: ReadonlyStringArray = ["Alice", "Bob", "Charlie"];

// myArray[0] = "Mallory"; // Error: Index signature in type 'ReadonlyStringArray' only permits reading
console.log(myArray[0]); // Alice

Interfaces with Tuples

Interfaces can be used with tuples to define a fixed length array with specific types for each element.

interface StringNumberTuple {
    0: string;
    1: number;
    length: 2; // this property ensures it’s a tuple of exactly length 2
}

let myTuple: StringNumberTuple = ["hello", 10]; // OK
// let myTuple: StringNumberTuple = ["hello", 10, 20];
// Error: Type '[string, number, number]' is not assignable to type 'StringNumberTuple'

Conclusion

Interfaces in TypeScript provide a powerful way to define and enforce the shape of objects and classes in your code. By using interfaces, you can improve the readability, maintainability, and robustness of your code. Whether you are working on a small project or a large-scale application, interfaces can help you write more reliable and maintainable code.