Introduction
TypeScript enhances JavaScript by adding type definitions, one powerful feature is the ability to define methods and their parameters within an interface to enforce structure on classes and objects. In this tutorial, we’ll explore how to use methods and parameters within TypeScript interfaces.
Defining Interface Methods
Interfaces in TypeScript allow us to define a contract within our code. We start by creating an interface with a method signature.
interface UserGreeter {
greet(name: string): string;
}
Any object or class that implements this interface is required to provide an implementation of the greet
method exactly as it’s described.
class User implements UserGreeter {
greet(name: string) {
return `Hello, ${name}!`;
}
}
const user = new User();
console.log(user.greet('Alice')); // 'Hello, Alice!'
Working with Method Parameters
Method parameters within interfaces can be given types, default values, and marked as optional. Let’s explore these features.
Typed Method Parameters
interface Calculator {
add(x: number, y: number): number;
}
// Implementing class
class SimpleCalculator implements Calculator {
add(x: number, y: number): number {
return x + y;
}
}
Optional Parameters
interface LoggerInterface {
log(message: string, userId?: string): void;
}
class Logger implements LoggerInterface {
log(message: string, userId?: string) {
if (userId) {
console.log(`${userId}: ${message}`);
} else {
console.log(`Guest: ${message}`);
}
}
}
Default Parameters
interface Greeting {
sayHello(name: string, greeting: string = 'Hello'): void;
}
// Note: Interface itself can't contain the default parameter value; it's in the implementing class
class EnglishGreeter implements Greeting {
sayHello(name: string, greeting: string = 'Hello'): void {
console.log(`${greeting}, ${name}!`);
}
}
Advanced Method Signatures
TypeScript interfaces are not limited to simple methods; they can also describe more complex behaviors.
Function Overloading in Interfaces
interface SearchFunc {
(source: string, subString: string): boolean;
(source: string, subStrings: string[]): boolean;
}
// Function implementing the overloaded interface
function search(source: string, subString: string | string[]): boolean {
if (typeof subString === 'string') {
return source.includes(subString);
} else {
return subString.some(sub => source.includes(sub));
}
}
Indexable Types
interface StringArray {
[index: number]: string;
length: number;
}
let myArray: StringArray;
myArray = ['Bob', 'Fred'];
let myStr: string = myArray[0];
Implementing Interfaces with Classes and Functions
Beyond simple type checking, interfaces also blend seamlessly with classes and functions.
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) {
this.currentTime = d;
}
}
function createClock(ctor: { new(): ClockInterface }): ClockInterface {
return new ctor();
}
let digitalClock = createClock(Clock);
Extending Interfaces
Interfaces can extend one another, allowing for the composition of complex types and functionality.
interface Shape {
draw(): void;
}
interface PenStroke {
penWidth: number;
}
interface Circle extends Shape, PenStroke {
radius: number;
}
let circle: Circle;
Generics with Interfaces
Generics provide the ability to use an interface with various types while still retaining type safety.
interface ResultContainer<T> {
result: T;
add: (a: T, b: T) => T;
}
class NumberResultContainer implements ResultContainer<number> {
result: number;
add(a: number, b: number): number {
return a + b;
}
}
Conclusion
Understanding and utilizing TypeScript interface methods and method parameters is fundamental for leveraging TypeScript’s full power. These capabilities enable us to design clear, type-safe APIs and contracts within our codebases. It’s a cornerstone of TypeScript’s structural type system and an indispensable tool for writing robust applications.