Why Clean Code in TypeScript is Not a Luxury, but a Necessity
Every developer has at least once encountered a situation: you open a project written a couple of months ago and don't understand what's going on. Variables are named a, b, data, functions are 200 lines long, and types are used sporadically. Sound familiar?
TypeScript gives us a powerful tool — static typing. But by itself, it doesn't make code clean. You can write terrible code in TypeScript, just as in any other language. Clean code is a discipline, a style, and a sign of respect for yourself and your colleagues.
In this article, we'll break down how to write clean code in TypeScript that is easy to read, maintain, and scale. You'll learn about SOLID principles, strong typing, naming conventions, working with interfaces, and much more. Let's go!
1. Naming — The Face of Your Code
Proper naming of variables, functions, and classes is 50% of success. A good name should answer the questions: "What is this?" or "What does this function do?".
Variables and Constants
Use nouns and camelCase. Avoid single-letter names (except in loops).
// Badconst d = new Date();const u = users.find(u => u.id === 5);
// Goodconst currentDate = new Date();const activeUser = users.find(user => user.id === 5);Functions
Function names should start with a verb. This immediately makes it clear what action is being performed.
// Badfunction data(id: number) { ... }
// Goodfunction fetchUserData(id: number): Promise<UserData> { ... }Classes and Interfaces
Classes are nouns in PascalCase. Interfaces are also, but often with the I prefix (though there is debate in the community). It's better to just give a clear name without a prefix.
// Badinterface IUser { name: string;}
// Goodinterface User { name: string; email: string;}2. Strong Typing — Your Best Friend
TypeScript was created so that types work for you. Do not use any unless absolutely necessary. Every time you write any, you disable the compiler's protection.
Avoid any
// Badfunction process(data: any) { return data.name;}
// Goodinterface Item { name: string; price: number;}
function process(data: Item): string { return data.name;}Use Union and Intersection Types
When a variable can accept multiple types, use Union (|). For combinations, use Intersection (&).
type Status = 'active' | 'inactive' | 'pending';type AdminUser = User & { role: 'admin' };Generics — Universal Functions
Generics allow you to write flexible code without losing type safety.
function firstElement<T>(arr: T[]): T | undefined { return arr[0];}
const num = firstElement([1, 2, 3]); // numberconst str = firstElement(['a', 'b']); // string3. SOLID — Principles for Professionals
The SOLID principles help build flexible and maintainable architecture. Let's look at each one using TypeScript examples.
Single Responsibility (SRP)
A class or function should have only one reason to change.
// Bad: the class does everythingclass UserService { createUser() { ... } sendEmail() { ... } generateReport() { ... }}
// Good: separate responsibilitiesclass UserCreator { ... }class EmailService { ... }class ReportGenerator { ... }Open/Closed (OCP)
Classes are open for extension but closed for modification. Use interfaces and inheritance.
Liskov Substitution (LSP)
Subclasses should be able to replace the base class without changing the program's behavior.