22
DecInterface in TypeScript :Explained
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.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.Read More - Typescript Interview Questions And Answers |
Use Cases of TypeScript Interfaces
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
Output
X: 10, Y: 20
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(): void {
console.log(`${this.name} barks`);
}
}
// Example usage:
const dog = new Dog("Buddy");
dog.speak()
Output
Buddy 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!");
}
}
// Works as expected
launch(new Bird());
const plane: Flyer = {
fly: () => console.log("Zoom!")
};
// Works with a plain object that matches the Flyer interface
launch(plane);
Output
Flap, flap!
Zoom!
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 interface
const result = add(5, 3); // Works as expected, result is 8
console.log("Addition Result:", result);
function performOperation(op: MathOperation, x: number, y: number): 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
console.log("Multiplication Result:", product);
Output
Addition Result: 8
Multiplication Result: 8
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): void {
console.log(`Name: ${user.name}`);
if (user.age !== undefined) {
console.log(`Age: ${user.age}`); // Safe check for optional property
}
if (user.bio !== undefined) {
console.log(`Bio: ${user.bio}`); // Additional check for another optional property
}
}
// Display information for both users
displayUserInfo(user1); // Prints name and age
displayUserInfo(user2); // Prints only the name
Output
Name: Alice
Age: 30
Name: Bob
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:
// This line would raise an error:
// shirt.id = "XYZ456"; // Uncommenting this line will throw a TypeScript error
// Allowed only through creating a new object:
const newShirt = Object.assign({}, shirt, { id: "XYZ456" });
console.log("Updated shirt:", shirt);
console.log("New shirt:", newShirt);
Output
Updated shirt: { id: 'ABC123', name: 'Polo shirt', price: 25 }
New shirt: { id: 'XYZ456', name: 'Polo shirt', price: 25 }
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 = {
age: 30,
salary: 50000,
performanceBonus: 1000, // Added this for better compliance with type safety
};
// Accessing data by string key
const employeeAge = employeeData["age"]; // Accessing age by key
console.log("Employee Age:", employeeAge);
// Adding a new key-value pair with a dynamic key
const bonusKey = "performanceBonus";
employeeData[bonusKey] = 1500; // Updating the performance bonus
console.log("Updated Performance Bonus:", employeeData[bonusKey]);
// Printing the entire object
console.log("Employee Data:", employeeData);
Output
Employee Age: 30
Updated Performance Bonus: 1500
Employee Data: { age: 30, salary: 50000, performanceBonus: 1500 }
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 the same types
}
const calculator: MathOperation = {
add(a, b) {
return a + b;
},
// No need to define 'multiply' initially, as it's optional
};
const sum = calculator.add(5, 3); // Works as expected (5 + 3 = 8)
console.log("Sum:", sum);
// To add `multiply`, we need to use a new object with all properties merged:
const extendedCalculator: MathOperation = {
...calculator,
multiply: (a, b) => a * b,
};
if (extendedCalculator.multiply) {
const product = extendedCalculator.multiply(2, 4); // Now works (2 * 4 = 8)
console.log("Product:", product);
}
Output
Sum: 8
Product: 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 a 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."
Output
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 Person
console.log(john.work()); // Uses method specific to Employee
Output
John Doe
I'm coding in TypeScript!
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).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)
Output
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
// Generic function to swap the types
function swap<T, U>(pair: Pair<T, U>): Pair<U, T> {
return { first: pair.second, second: pair.first };
}
const swappedPair = swap(stringPair); // Swaps types, resulting in { first: "World", second: "Hello" }
console.log(swappedPair); // { first: "World", second: "Hello" }
Output
{ 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 interface
// Function that accepts any function matching the MathOperation interface
function performOperation(op: MathOperation, x: number, y: number): number {
return op(x, y); // Calls the passed operation function
}
const result = performOperation(add, 5, 3); // Uses the 'add' function
console.log(result); // Prints 8 (5 + 3)
Output
8
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
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.