Understanding TypeScript class methods is essential for any developer looking to build scalable, type-safe applications. In this comprehensive guide, we’ll explore everything you need to know about implementing and using class methods in TypeScript, from basic syntax to advanced patterns.
Table of Contents
- Understanding TypeScript Class Methods
- Instance Methods vs Static Methods
- Method Modifiers
- Method Overloading
- Getter and Setter Methods
- Asynchronous Methods
- Method Decorators
- Best Practices
- Common Pitfalls to Avoid
- Conclusion
Understanding TypeScript Class Methods
Class methods are functions that belong to a class and define its behavior. In TypeScript, methods provide additional type safety and enhanced tooling support compared to regular JavaScript classes.
class Calculator {
// Instance method
add(a: number, b: number): number {
return a + b;
}
// Static method
static multiply(a: number, b: number): number {
return a * b;
}
}
Code language: JavaScript (javascript)
Instance Methods vs Static Methods
TypeScript supports both instance and static methods, each serving different purposes in your class design.
Instance Methods
Instance methods are functions that operate on instance-specific data and are called on class instances:
class User {
private name: string;
constructor(name: string) {
this.name = name;
}
greet(): string {
return `Hello, ${this.name}!`;
}
}
const user = new User('John');
console.log(user.greet()); // Output: Hello, John!
Code language: JavaScript (javascript)
Static Methods
Static methods belong to the class itself and can be called without creating an instance:
class MathOperations {
static square(x: number): number {
return x * x;
}
static isPositive(x: number): boolean {
return x > 0;
}
}
console.log(MathOperations.square(5)); // Output: 25
console.log(MathOperations.isPositive(-3)); // Output: false
Code language: JavaScript (javascript)
Method Modifiers
TypeScript provides access modifiers to control method visibility:
class Employee {
private salary: number;
constructor(salary: number) {
this.salary = salary;
}
// Public method - accessible from anywhere
public getAnnualSalary(): number {
return this.calculateMonthlySalary() * 12;
}
// Private method - only accessible within the class
private calculateMonthlySalary(): number {
return this.salary;
}
// Protected method - accessible within class and subclasses
protected calculateBonus(): number {
return this.salary * 0.1;
}
}
Code language: JavaScript (javascript)
Method Overloading
TypeScript supports method overloading, allowing you to define multiple method signatures:
class StringManipulator {
reverse(str: string): string;
reverse(arr: string[]): string[];
reverse(stringOrArray: string | string[]): string | string[] {
if (typeof stringOrArray === 'string') {
return stringOrArray.split('').reverse().join('');
}
return [...stringOrArray].reverse();
}
}
const manipulator = new StringManipulator();
console.log(manipulator.reverse('hello')); // Output: olleh
console.log(manipulator.reverse(['a', 'b', 'c'])); // Output: ['c', 'b', 'a']
Code language: JavaScript (javascript)
Getter and Setter Methods
TypeScript provides getter and setter methods for controlled property access:
class BankAccount {
private _balance: number = 0;
get balance(): number {
return this._balance;
}
set balance(newBalance: number) {
if (newBalance < 0) {
throw new Error('Balance cannot be negative');
}
this._balance = newBalance;
}
}
const account = new BankAccount();
account.balance = 100; // Uses setter
console.log(account.balance); // Uses getter, Output: 100
Code language: JavaScript (javascript)
Asynchronous Methods
TypeScript fully supports async methods using async/await syntax:
class DataFetcher {
async fetchUserData(userId: string): Promise<any> {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
return await response.json();
} catch (error) {
throw new Error(`Failed to fetch user data: ${error.message}`);
}
}
}
Code language: JavaScript (javascript)
Method Decorators
TypeScript decorators can be used to modify or enhance method behavior:
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.log(`Calling ${propertyKey} with arguments:`, args);
const result = originalMethod.apply(this, args);
console.log(`Method ${propertyKey} returned:`, result);
return result;
};
}
class Calculator {
@log
multiply(a: number, b: number): number {
return a * b;
}
}
Code language: JavaScript (javascript)
Best Practices
- Always specify return types explicitly:
class Example {
getData(): Promise<string> {
return Promise.resolve('data');
}
}
Code language: JavaScript (javascript)
- Use method modifiers appropriately:
class User {
private password: string;
public updatePassword(newPassword: string): void {
this.validatePassword(newPassword);
this.password = newPassword;
}
private validatePassword(password: string): void {
if (password.length < 8) {
throw new Error('Password too short');
}
}
}
Code language: JavaScript (javascript)
- Implement proper error handling:
class FileHandler {
async readFile(path: string): Promise<string> {
try {
// File reading logic
return 'file contents';
} catch (error) {
throw new Error(`Failed to read file: ${error.message}`);
}
}
}
Code language: JavaScript (javascript)
Common Pitfalls to Avoid
- Forgetting to bind methods when using them as callbacks
- Not properly handling ‘this’ context in methods
- Mixing instance and static methods inappropriately
- Overusing public methods when private would be more appropriate
Conclusion
Mastering TypeScript class methods is crucial for writing maintainable, type-safe object-oriented code. By understanding the different types of methods, their modifiers, and best practices, you can create more robust and scalable applications.
Put your knowledge into practice by refactoring an existing JavaScript class to use TypeScript’s method features. Try implementing different types of methods and experiment with access modifiers to better understand their impact on your code’s structure and maintainability.