Clean Code in TypeScript: Principles and Examples for Practitioners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Clean Code in TypeScript: Principles and Practical Examples



Clean Code is not just a trendy term, but a fundamental skill for a professional developer. In the context of TypeScript, which adds static typing to the dynamic world of JavaScript, clean code principles become even more important. TypeScript gives us powerful tools for self-documenting code, but without the right approach, they can turn into chaos.



In this article, we will break down the key Clean Code principles as applied to TypeScript and look at practical examples that will help you write more readable, maintainable, and reliable code.



1. Naming: Speak the Language of Business



The first and most obvious Clean Code principle is proper naming. The names of variables, functions, classes, and interfaces should reflect their essence and the developer's intent. TypeScript, with its strict typing, allows you to make names even more informative.



Bad example:

// What does this function do? How long does it take to understand?function d(a: number, b: number): number {    return a * b - (a + b);}

// And what is this interface?interface I { n: string; a: number; e: string;}


Good example:

// Function for calculating a discount considering a thresholdfunction calculateDiscountedPrice(price: number, discountPercent: number): number {    const discountAmount = price * (discountPercent / 100);    return price - discountAmount;}

// User interface with explicit fieldsinterface UserProfile { name: string; age: number; email: string;}


Rules for TypeScript:

  • Interfaces — use nouns without the I prefix (e.g., User, not IUser). Modern TypeScript and ESLint recommend this approach.
  • Types — for complex unions, use the | operator and meaningful names: StatusType, ApiResponse.
  • Boolean variables — start with is, has, should: isActive, hasPermission.
  • Functions — use verbs: getUserData, validateEmail, processOrder.


2. SOLID in TypeScript: Practical Application



The SOLID principles (Single Responsibility, Open-Closed, Liskov Substitution, Interface Segregation, Dependency Inversion) fit perfectly with TypeScript thanks to its support for interfaces, abstract classes, and generics.



Single Responsibility Principle (SRP): A class should have only one reason to change.



// SRP violation: the class does too muchclass UserService {    createUser(data: any) { /* ... */ }    sendEmail(email: string) { /* ... */ }    generateReport() { /* ... */ }}

// Correct approach: separation of concernsclass UserRepository { save(user: User): void { /* ... */ }}

class EmailService { sendWelcomeEmail(email: string): void { /* ... */ }}

class ReportGenerator { generateUserReport(users: User[]): Report { /* ... */ }}


Liskov Substitution Principle (LSP): Subclasses should be interchangeable with their base class. In TypeScript, this is especially relevant when working with inheritance.



// LSP violation: subclass narrows the contractclass Bird {    fly(): void { console.log('Flying'); }}

class Penguin extends Bird { fly(): void { throw new Error('Penguins cannot fly!'); }}

// Correct: extract an interfaceinterface Flyable { fly(): void;}

class Bird implements Flyable { fly(): void { console.log('Flying'); }}

class Penguin { s

Blogs

Book Recommendations