Interface in TypeScript :Explained

Interface in TypeScript :Explained

13 Nov 2024
Advanced
23.4K Views
24 min read
Learn with an interactive course and practical hands-on labs

TypeScript Programming Course

Interface in TypeScript

TypeScript's interface functions as an arrangement for your code, guaranteeing that objects and classes stick to particular structures. Consider building a house. Every room should have a door, windows, and possibly a light switch. Similarly, interfaces define which attributes or methods an object should have, ensuring that your code is consistent and manageable.

In this TypeScript tutorial, we will look into TypeScript Interfaces we will also 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 clearer, simpler, 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.

TypeScript Interfaces Syntax

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;
}

Breakdown of the syntax variables:

  • Interface: A keyword used to describe an interface name.
  • interface_name: The interface's name, as defined by the TypeScript naming standard.
  • Property 1 and Property 2 are interface properties.
  • type: A TypeScript type annotation indicating the type of each property.

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.

Use Cases of TypeScript Interfaces

TypeScript interfaces are powerful tools for defining object structures, ensuring that objects follow a consistent shape. They also enforce contracts for classes, requiring specific methods or properties. Interfaces can define function signatures, making the code more predictable and easier to understand. Additionally, they allow for extending types and are useful in specifying the structure of API responses, ensuring data consistency across your application.

1. How to Define Object Structures

TypeScript interfaces allow you to define the shape of an object by specifying its properties and types.

Example:


interface User {
    name: string;
    age: number;
    email: string;
}
const user: User = {
    name: "Alice",
    age: 30,
    email: "alice@example.com",
};   

This ensures that any object assigned to the User type must include the defined properties with the correct types.

2. How to Type-Check Function Parameters

You can use interfaces to type-check the parameters of a function, ensuring that the arguments passed match the expected structure.

Example:


interface Point {
    x: number;
    y: number;
}
function printPoint(point: Point) {
    console.log(`X: ${point.x}, Y: ${point.y}`);
}
printPoint({ x: 10, y: 20 }); // Type-checked against the Point interface

This guarantees that only objects matching the Point interface can be passed to printPoint().

3. How to Implement Class Contracts

Interfaces can define the structure that a class must adhere to, ensuring consistent behavior across implementations.

Example:

interface Animal {
    name: string;
    speak(): void;
}

class Dog implements Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    speak() {
        console.log(`${this.name} barks`);
    }
} 

The Dog class must implement both the name property and the speak() method as specified by the Animal interface, enforcing a contract.

4. 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 expectedconst 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.

5. 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 interfaceconst result = add(5, 3); // Works as expected, result is 8function 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.

6. 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.

7. Read-only Properties in Interfaces

TypeScript interfaces can declare properties as "read-only" by using the read-only 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 read-only, 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).

8. 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 nameconst 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.

9. 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.

10. 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.

11. How to Extend Interfaces?

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 Personconsole.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.

12. 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.

13. 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.

14. 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 interfacefunction 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' functionconsole.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.
Conclusion
We have explored interfaces in Typescript, And learned that 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

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.


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.

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 Typescript skill challenge to evaluate yourself!

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

GET FREE 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 9th time in a row (2016-2024). 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