09
NovInterface in TypeScript
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).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?
Q2. In TypeScript, what is the difference between classes and interfaces?
Q3. What is the purpose of the interface?
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.