Clean Code на TypeScript: как писать код, который не стыдно показать
Чистый код (Clean Code) — это не просто модный термин, а фундаментальный подход к разработке, который превращает хаотичный набор инструкций в понятную и поддерживаемую систему. Для TypeScript, как статически типизированного языка, принципы чистого кода приобретают особое значение: строгая типизация и мощные инструменты компилятора позволяют выявлять ошибки на этапе разработки, но только если код структурирован правильно. В этой статье мы разберем ключевые принципы Clean Code применительно к TypeScript, подкрепив их конкретными примерами.
1. Именование переменных, функций и классов
Имя переменной или функции должно отвечать на вопрос "что это?" или "что делает?". В TypeScript, благодаря типизации, имена становятся еще более информативными. Избегайте однобуквенных имен (кроме индексов циклов) и сокращений, которые понятны только вам.
// Плохо
const d = new Date();
const fn = (a: number, b: number) => a + b;
// Хорошо
const currentDate = new Date();
const sum = (firstNumber: number, secondNumber: number): number => firstNumber + secondNumber;
Для классов используйте существительные, для функций — глаголы или глагольные конструкции. Булевы переменные должны начинаться с is, has, should.
// Плохо
const flag = true;
function validate() { /* ... */ }
// Хорошо
const isUserActive = true;
function validateEmail(email: string): boolean { /* ... */ }
2. Принцип единственной ответственности (SRP) для функций
Каждая функция должна делать ровно одну вещь и делать её хорошо. Если функция выполняет несколько действий — разбейте её на более мелкие. TypeScript позволяет строго типизировать входные и выходные данные, что облегчает рефакторинг.
// Плохо: функция ищет пользователя, проверяет пароль и отправляет email
async function loginUser(email: string, password: string): Promise<void> {
const user = await findUserByEmail(email);
if (user && user.password === password) {
await sendLoginEmail(user.email);
console.log('Login successful');
}
}
// Хорошо: разделяем ответственность
async function findUserByEmail(email: string): Promise<User | null> { /* ... */ }
function isPasswordValid(inputPassword: string, storedHash: string): boolean { /* ... */ }
async function notifyUserLogin(email: string): Promise<void> { /* ... */ }
async function loginUser(email: string, password: string): Promise<boolean> {
const user = await findUserByEmail(email);
if (!user || !isPasswordValid(password, user.passwordHash)) {
return false;
}
await notifyUserLogin(user.email);
return true;
}
3. Использование типов и интерфейсов вместо any
Одна из главных фишек TypeScript — система типов. Избегайте any любой ценой. Если тип неизвестен заранее, используйте unknown, generics или объединения типов. Это делает код самодокументируемым и защищает от ошибок.
// Плохо
function getData(id: any): any {
return fetch(`/api/${id}`);
}
// Хорошо
interface ApiResponse<T> {
data: T;
error?: string;
}
async function getData<T>(id: string): Promise<ApiResponse<T>> {
const response = await fetch(`/api/${id}`);
return response.json();
}
Используйте строгие интерфейсы для описания структуры объектов. Это упрощает рефакторинг и помогает IDE подсказывать свойства.
interface User {
readonly id: number;
name: string;
email: string;
role: 'admin' | 'user';
}
function updateUserName(user: User, newName: string): User {
return { ...user, name: newName };
}
4. Обработка ошибок и защитное программирование
Чистый код предусматривает явную обработку исключительных ситуаций. Не игнорируйте возможные ошибки. Используйте Result-типы или специальные классы ошибок.
// Плохо
function divide(a: number, b: number): number {
return a / b; // Может быть Infinity или NaN
}
// Хорошо
function divide(a: number, b: number): number {
if (b === 0) {
throw new Error('Division by zero is not allowed');
}
return a / b;
}
// Альтернатива с Result-типом
type Result<T> = { success: true; value: T } | { success: false; error: string };
function safeDivide(a: number, b: number): Result<number> {
if (b === 0) {
return { success: false, error: 'Division by zero' };
}
return { success: true, value: a / b };
}
5. Комментарии — только когда код не может быть понятнее
Чистый код — это самодокументируемый код. Комментарии должны объяснять "почему", а не "как". Если вы пишете комментарий к простому действию — возможно, стоит переименовать переменную или выделить логику в отдельную функцию.
// Плохо: комментарий объясняет очевидное
// Прибавляем 1 к счетчику
counter += 1;
// Хорошо: комментарий объясняет бизнес-логику
// Скидка применяется только для пользователей с премиум-статусом
if (user.plan === 'premium') {
applyDiscount(order, 0.1);
}
Заключение
Clean Code на TypeScript — это не набор догм, а инструмент, который делает вашу работу проще и приятнее. Следуя принципам осмысленного именования, единственной ответственности, строгой типизации и грамотной обработки ошибок, вы создаете код, который легко читать, тестировать и поддерживать. Начните с малого: переименуйте одну неочевидную переменную, разбейте большую функцию на две — и вы почувствуете разницу. Помните: код пишется для людей, а компилятор — лишь инструмент проверки.