Clean Code на TypeScript: принципы и примеры на практике
Каждый разработчик сталкивался с ситуацией, когда через месяц после написания кода его собственный скрипт кажется чужим и непонятным. В мире TypeScript, где строгая типизация даёт преимущество, принципы Clean Code становятся особенно важными. Чистый код — это не просто эстетика, а инструмент для уменьшения багов, ускорения разработки и облегчения командной работы. В этой статье мы разберём ключевые принципы Clean Code на TypeScript с реальными примерами, которые помогут вам писать код, который легко читать, тестировать и поддерживать.
1. Именование: говорите, что делает переменная
Первое, что видит разработчик — это имена переменных, функций и классов. Плохое имя заставляет читателя тратить время на расшифровку. В TypeScript с его системой типов имя должно быть самодокументируемым.
Плохой пример:
const d = new Date();
const arr = [1, 2, 3];
function proc(x: number, y: number) {
return x * y;
}
Хороший пример:
const currentDate = new Date();
const userScores: number[] = [1, 2, 3];
function calculateArea(width: number, height: number): number {
return width * height;
}
Используйте существительные для переменных и глаголы для функций. Избегайте сокращений (кроме общепринятых, таких как id или url). TypeScript позволяет добавить типы, но имя всё равно должно отражать суть.
2. Функции: маленькие, чистые и предсказуемые
Функция должна делать только одну вещь. Если вы не можете придумать короткое имя для функции — скорее всего, она делает слишком много. Также важно избегать побочных эффектов.
Принцип единой ответственности (SRP) на уровне функций:
// Плохо: функция делает всё сразу
function processUserData(user: User): void {
// Валидация
if (!user.email) throw new Error('Email required');
// Сохранение в БД
database.save(user);
// Отправка email
emailService.sendWelcome(user.email);
}
// Хорошо: разделяем ответственность
function validateUser(user: User): boolean {
return user.email !== undefined;
}
function saveUser(user: User): void {
database.save(user);
}
function sendWelcomeEmail(email: string): void {
emailService.send(email);
}
function processUser(user: User): void {
if (!validateUser(user)) {
throw new Error('Invalid user data');
}
saveUser(user);
sendWelcomeEmail(user.email);
}
Также избегайте большого количества параметров. Если их больше двух — объедините в объект:
// Плохо
function createUser(name: string, email: string, age: number, isAdmin: boolean): void {}
// Хорошо
interface CreateUserParams {
name: string;
email: string;
age: number;
isAdmin: boolean;
}
function createUser(params: CreateUserParams): void {}
3. Типизация: используйте возможности TypeScript на полную
Одно из главных преимуществ TypeScript — строгая типизация. Но многие разработчики используют any как костыль, убивая всю пользу. Чистый код на TypeScript — это код, где типы явно описаны, а не угадываются.
Избегайте any:
// Плохо
function getData(): any {
return fetch('/api/data');
}
// Хорошо
interface ApiResponse {
id: number;
title: string;
}
async function getData(): Promise<ApiResponse> {
const response = await fetch('/api/data');
return response.json();
}
Используйте discriminated unions для сложных состояний:
// Плохо: неясно, что в объекте
type Status = 'loading' | 'success' | 'error';
interface State {
status: Status;
data?: Data;
error?: string;
}
// Хорошо: каждый статус — отдельный интерфейс
interface LoadingState { status: 'loading'; }
interface SuccessState { status: 'success'; data: Data; }
interface ErrorState { status: 'error'; error: string; }
type State = LoadingState | SuccessState | ErrorState;
function handleState(state: State) {
switch (state.status) {
case 'loading':
console.log('Загрузка...');
break;
case 'success':
console.log('Данные:', state.data);
break;
case 'error':
console.error('Ошибка:', state.error);
break;
}
}
Такой подход делает код безопасным и самодокументируемым — вы точно знаете, какие поля доступны в каждом состоянии.
4. Комментарии и документация: меньше — лучше
Одно из правил Clean Code гласит: «Хороший код сам себя документирует». Комментарии не должны объяснять что делает код (это должно быть ясно из названий и структуры), а почему он делает это именно так.
Плохо:
// Увеличиваем счётчик на 1
counter += 1;
// Проверяем, что пользователь авторизован
if (user.isLoggedIn) { ... }
Хорошо:
// Комментарий объясняет бизнес-логику, а не код
// Используем Math.floor, потому что API округляет в меньшую сторону
const finalPrice = Math.floor(price * discount);
// TODO: Удалить после миграции на новую версию API (v2)
function legacyAuth() { ... }
Для публичных API используйте JSDoc, но не злоупотребляйте:
/**
* Рассчитывает стоимость заказа с учётом скидки.
* @param basePrice - базовая цена
* @param discount - процент скидки (0-100)
* @returns Итоговая стоимость
*/
function calculateDiscountedPrice(basePrice: number, discount: number): number {
return basePrice * (1 - discount / 100);
}
Заключение
Clean Code на TypeScript — это не набор жёстких правил, а философия, которая делает вашу жизнь проще. Начните с малого: давайте переменным осмысленные имена, разбивайте большие функции на маленькие, используйте строгую типизацию и не пишите комментарии там, где код говорит сам за себя.
Помните: вы пишете код не для компьютера, а для людей — включая себя через полгода. Применяя эти принципы на практике, вы заметите, как уменьшается количество багов, а рефакторинг перестаёт быть страшным. Начните прямо сегодня — возьмите один из своих проектов и попробуйте применить хотя бы один принцип из этой статьи.
Если вы хотите углубиться в тему, рекомендую книгу Роберта Мартина