Interface in TypeScript

Interface in TypeScript

22 Dec 2023
Advanced
20.7K Views
13 min read
Learn via Video Course & by Doing Hands-on Labs

TypeScript Programming Course

Interface in TypeScript: An Overview

Hey! Have you ever wished your TypeScript code was better organized and simple to understand? In this TypeScript tutorial on interfaces, discover how they act as friendly blueprints, helping to define the structure of the objects you have.  You can use interfaces to make your code more clear, simple, and ready for smooth collaboration between different portions.

What is an Interface in Typescript?

In TypeScript, an interface is a blueprint for objects, specifying their attributes and methods in the same way that a contract does. It does not store data, but it serves as a type check to guarantee that objects conform to its structure, improving code clarity and discovering mistakes early.

Why use Interfaces?

  • Type checks for objects: Ensures data structure consistency and detects mistakes early.
  • Clarity of code: Improves comprehension of desired object behavior.
  • Function parameter/return types: These define the function's expected input and output.
  • Code reuse and abstraction: Allows for code reuse by using interchangeable objects that adhere to the interface.
  • Decoupling and flexibility: Encourages loose coupling and flexible object implementation.

Interface Declaration

In TypeScript, an interface declaration provides the structure of an object, acting as a blueprint, identifying its properties and functions but without implementing them.

Syntax

interface <interface_name> {

// Properties (required & optional)

property1: type1;

property2?: type2;

// Methods (no implementation)

method1(param1: type3, param2: type4): type5;

method2?(param6?: type6): void;

}

Example of Interface

interface Person {

name: string;

age: number;

}

const employee: Person = {

name: "John Doe",

age: 30,

};

The Person interface requires an object to contain both name and age properties. This contract is fulfilled by the employee object, which provides values for both properties.

Duck Typing/Structural Subtyping in TypeScript

The type system in TypeScript focuses on an object's structure and properties rather than its exact class or type. This means that any object with the necessary characteristics and methods, regardless of origin, can be used.

Example of Duck Typing/Structural Subtyping in TypeScript


interface Flyer {

fly(): void;

}

function launch(flyer: Flyer) {

flyer.fly(); // Works with any object implementing the "fly" method

}

class Bird implements Flyer {

fly() {

console.log("Flap, flap!");

}

}

launch(new Bird()); // Works as expected

const plane = { fly: () => console.log("Zoom!"); };

launch(plane); // Also works, even though plane isn't a Bird!

The Flyer interface is defined by a single-flymethod. The launch function accepts any object that implements Flyer, independent of class. This enables items such as planes (including non-birds) to be used in the same way that actual birds are, as long as they have the necessary fly technique.

Function Interfaces in TypeScript

Function interfaces specify a function's expected structure and behavior. They define the parameters, return type, & optional features such as rest parameters.

Example of Function Interfaces in TypeScript

interface MathOperation {

(a: number, b: number): number; // Type signature for a function taking two numbers and returning a number

}

const add: MathOperation = (a, b) => a + b; // Function implementing the MathOperation interface

const multiply: MathOperation = (a, b) => a * b; // Another function adhering to the interface

const result = add(5, 3); // Works as expected, result is 8

function performOperation(op: MathOperation, x: number, y: number) {

return op(x, y); // Function accepting any function conforming to the MathOperation interface

}

const product = performOperation(multiply, 2, 4); // Uses multiply function with performOperation

The MathOperation interface is defined by a type signature that specifies two number parameters and a number return type. We define add and multiply functions to implement the interface. The performOperation function accepts any function that conforms to the MathOperation interface, giving you the flexibility to use alternative operations.

Optional Properties in Interfaces

Optional properties can be added to TypeScript interfaces by including a "?" after the property name. This means that the property isn't necessarily necessary to be present in an interface-compliant object.

Example of Optional Properties in Interfaces

interface User {

name: string; // Required property

age?: number; // Optional property

bio?: string; // Another optional property

}

const user1: User = { name: "Alice", age: 30 }; // Fulfills both required and optional properties

const user2: User = { name: "Bob" }; // Fulfills only the required property (age and bio omitted)

function displayUserInfo(user: User) {

console.log(`Name: ${user.name}`);

if (user.age) console.log(`Age: ${user.age}`); // Safe check for optional property

if (user.bio) console.log(`Bio: ${user.bio}`); // Additional check for another optional property

}

displayUserInfo(user1); // Prints both name and age

displayUserInfo(user2); // Prints only name (age and bio not accessed)

The user interface specifies name as a necessary property, whereas age and bio are designated as optional with a "?" The presence of optional properties varies between two user objects. The displayUserInfo function uses conditional checks to handle optional properties, eliminating errors when they are missing.

Read-only Properties in Interfaces

TypeScript interfaces can declare properties as "read-only" by using the readonly keyword, which assures that their values cannot be directly updated after the assignment.

Example of Read-only Properties in Interfaces

interface Product {

readonly id: string; // Read-only property (cannot be changed)

name: string; // Regular property (can be updated)

price: number; // Regular property

}

const shirt: Product = { id: "ABC123", name: "T-shirt", price: 20 };

// Can update regular properties:

shirt.name = "Polo shirt";

shirt.price = 25;

// Cannot directly modify read-only property:

// Uncaught TypeError: Cannot assign to read-only property 'id' of object '#<Object>'

shirt.id = "XYZ456"; // Raises error

// Allowed only through mutating the entire object:

const newShirt = Object.assign({}, shirt, { id: "XYZ456" });

The Product interface with id is marked as readonly, limiting its value after assignment. We create a shirt object that adheres to the interface. We can alter ordinary properties like name and price, but assigning a new value to ID fails. We can only "change" it by generating a new object with the desired id value (rather than directly editing the original shirt).

Indexable Properties in Interfaces

Using indexable attributes, TypeScript interfaces can behave like collections. These define how objects can be accessed using specified keys/indices, such as accessing array elements.

Example of Indexable Properties in Interfaces

interface StringMap {

[key: string]: number; // Allows any string key with a number value

}

const employeeData: StringMap = {

name: "Jane Doe",

age: 30,

salary: 50000,

};

const employeeName = employeeData["name"]; // Accessing data by string key

// You can also use string literals or dynamic keys:

const age = employeeData.age; // Accessing data by property name

const bonusKey = "performanceBonus";

employeeData[bonusKey] = 1000; // Adding a new key-value pair with dynamic key

// TypeScript type safety ensures key types and values match the interface definition

The StringMap interface defines an index signature by using [key: string] followed by the expected value type (in this example, number). We build an interface-compliant object employeeData to represent key-value pairs with string keys and number values. Using the indexer behavior, we access data using both string literal keys and dynamic keys.

Defining Function Types in Interfaces

Using a call signature, you can declare functions as attributes within interfaces. This defines the function's parameter and return types, as well as optional features such as optional parameters.

Example of Defining Function Types in Interfaces

interface MathOperation {

add(a: number, b: number): number; // Function signature for 'add' with two number params and a number return

multiply?: (a: number, b: number) => number; // Optional function signature for 'multiply' with same types

}

const calculator: MathOperation = {

add(a, b) { return a + b; },

// No need to define 'multiply' explicitly as it's optional

};

const sum = calculator.add(5, 3); // Works as expected (5 + 3 = 8)

// Define 'multiply' later if needed:

calculator.multiply = (a, b) => a * b;

const product = calculator.multiply(2, 4); // Now works too (2 * 4 = 8)

This code specifies the MathOperation interface for basic math functions, with add being required and multiply being optional. It generates a calculator object that implements MathOperation with add declared and uses it for addition and (later) dynamically adds multiply.

Using Interfaces with Classes in TypeScript

TypeScript interfaces can serve as class blueprints, detailing the attributes and methods that the class must implement. This ensures that the interface and the implementing class are consistent and type-safe.

Example of using Interfaces with Classes

interface Person {

name: string;

age: number;

greet(): string; // Method signature with no arguments and string return type

}

class Employee implements Person {

name: string;

age: number;

department: string; // Additional property not defined in the interface

constructor(name: string, age: number, department: string) {

this.name = name;

this.age = age;

this.department = department;

}

greet(): string {

return `Hello, my name is ${this.name} and I work in the ${this.department} department.`;

}

}

const john = new Employee("John Doe", 30, "Engineering");

console.log(john.greet()); // Prints "Hello, my name is John Doe and I work in the Engineering department."

The person interface defines essential properties and a greeting method, while the Employee class implements it with additional information and a custom "greet" message. Object "John" greets the Employee, containing department information as well as the conventional interface welcome.

Extending Interfaces in TypeScript

Using the extends keyword, you can extend one interface from another. This inherits all of the properties and methods from the base interface and enables for the addition of child-specific elements.

Example of Extending Interfaces in TypeScript

interface Person {

name: string;

age: number;

}

interface Employee extends Person {

department: string;

work(): string; // Additional method specific to Employee

}

const john: Employee = {

name: "John Doe",

age: 30,

department: "Engineering",

work() {

return "I'm coding in TypeScript!";

}

};

console.log(john.name); // Accesses inherited property from Person

console.log(john.work()); // Uses method specific to Employee

Interfaces specify which properties and methods should be available to an object. Employee inherits from Person and adds features such as department and work. As an employee, John possesses both general characteristics (name, age) and specific characteristics (department, work). You have access to both inherited and unique features.

How are type aliases different from interfaces in typescript?

Type aliases: Type aliases can be renamed, flexible (any type), intersectional, or closed (cannot be extended further).
Interfaces: Defining object shapes, contracting for attributes and methods, inheritance, and being open (allowing for extension).

Hybrid types in interfaces

In TypeScript interfaces, hybrid types combine object properties and function signatures into a single specification. This enables you to design interfaces for things that can both retain data and be called functions.

Example of Hybrid types in interfaces

interface Calculator {
sum(a: number, b: number): number; // Function to add two numbers
currentResult: number; // Property to store the current result
}
function createCalculator(): Calculator {
return {
sum(a, b) {
this.currentResult = a + b;
return this.currentResult;
},
currentResult: 0,
};
}
const calc = createCalculator();
const sumResult = calc.sum(5, 3); // Calling the "sum" function (5 + 3 = 8)
console.log(calc.currentResult); // Accessing the updated "currentResult" property (8)
The calculator interface defines expected functions (sum) and attributes (currentResult).createCalculator creates an object that implements Calculator and has sum logic and an initial result.

Using Generics in Interfaces

TypeScript's generic interfaces enable them to work with different kinds at runtime. Within the interface, you create placeholder types that can be filled with specified kinds when the interface is used.

Example of using Generics in Interfaces

interface Pair<T, U> { // `T` and `U` are type placeholders
first: T;
second: U;
}
const stringPair: Pair<string, string> = { first: "Hello", second: "World" }; // Specific usage with two strings
const numberPair: Pair<number, boolean> = { first: 3, second: true }; // Different types used
function swap<T, U>(pair: Pair<T, U>): Pair<U, T> { // Generic function using same interface
return { first: pair.second, second: pair.first };
}
const swappedPair = swap(stringPair); // Swaps types, resulting in { first: "World", second: "Hello" }
Pair is a generic interface with two values (first and second) of distinct types (T and U). Multiple pairs of different types (string and number) can be formed, and their values are exchanged independently of type using a generic swap function.

Callable Interfaces

TypeScript's callable interfaces allow you to define the type signature of a function as an interface. This enables you to treat functions as types, defining their expected parameters, return value, and extra characteristics such as optional parameters.

Example of Callable Interfaces

interface MathOperation {

(a: number, b: number): number; // Function signature, defines two number params and a number return

}

const add: MathOperation = (a, b) => a + b; // Function adhering to the interface

const multiply: MathOperation = (a, b) => a * b; // Another function conforming to the interface

function performOperation(op: MathOperation, x: number, y: number) {

return op(x, y); // Function accepting any function matching the MathOperation interface

}

const result = performOperation(add, 5, 3); // Uses 'add' function

console.log(result); // Prints 8 (5 + 3)

This code creates a versatile interface for the arithmetic operations (MathOperation) and functions (add, multiply) that come after it. A separate function (performOperation) applies any operation that matches the interface to any number, making it adaptable.
Summary
To address various cases, TypeScript provides interface elements such as optional attributes, read-only properties, indexers, and function types. Furthermore, interfaces connect smoothly with classes, allowing class blueprints to be written using interfaces. Inheritance between interfaces enhances their potential even more, while hybrid types and generics provide even more versatility.

FAQs

Q1. What is the purpose of the TypeScript interface method?

For type verification, Typescript Interfaces are utilized. This approach is also known as duck typing or structural subtyping. The keyword interface specifies an interface, which can be declared using a function or an arrow function.


Q2. In TypeScript, what is the difference between classes and interfaces?

As you can see, the primary distinction between TypeScript interfaces and classes is that interfaces describe an object's shape, whereas classes specify an object's behavior and properties.

Q3. What is the purpose of the interface?

An interface establishes a behavior protocol that any class may implement in the class hierarchy. Interfaces can be used for the following purposes: Capturing similarities between unconnected classes without demanding a class link.

Take our free typescript skill challenge to evaluate your skill

In less than 5 minutes, with our skill challenge, you can identify your knowledge gaps and strengths in a given skill.

GET CHALLENGE

Share Article
About Author
Shailendra Chauhan (Microsoft MVP, Founder & CEO at Scholarhat by DotNetTricks)

Shailendra Chauhan is the Founder and CEO at ScholarHat by DotNetTricks which is a brand when it comes to e-Learning. He provides training and consultation over an array of technologies like Cloud, .NET, Angular, React, Node, Microservices, Containers and Mobile Apps development. He has been awarded Microsoft MVP 8th time in a row (2016-2023). He has changed many lives with his writings and unique training programs. He has a number of most sought-after books to his name which has helped job aspirants in cracking tough interviews with ease.
Accept cookies & close this