Зачем вы всё ещё ставите «I» перед интерфейсами в TypeScript?

Многие до сих пор называют интерфейсы в TypeScript с буквы I: IUser, ICar, IRepository.
Так делали в C#, но в TypeScript это не нужно — и даже вредно.
Префикс I портит читаемость, ломает инкапсуляцию и поощряет лень в именах.
Разберём, почему от этой привычки пора избавиться.
Откуда взялся этот префикс
В C# интерфейсы начинались с I из-за старой технологии COM.
TypeScript не имеет с ней ничего общего.
IDE и сам язык показывают типы без всяких префиксов, поэтому I — просто визуальный шум.
1. Венгерская нотация умерла
Префикс I — наследие венгерской нотации, когда имена отражали тип:
sName: string
iCount: number
bVisible: booleanТогда это помогало, но сегодня тип и так виден в IDE.
Дублировать его в названии бессмысленно.
TypeScript-команда прямо пишет в своих гайдлайнах: не используйте I перед интерфейсами — это не добавляет смысла.
2. Нарушение инкапсуляции
Когда вы вызываете метод, вам не важно, что перед вами — интерфейс или класс.
Важен контракт, а не форма.
function startEngine(car: Car) {
car.drive();
}Если потом вы решите заменить интерфейс на базовый класс, придётся переименовать все ICar на Car.
Префикс создаёт лишнюю зависимость от деталей реализации.
3. Лень в названиях
class Car implements ICar {}
Знакомо? Так проще, чем придумать нормальное имя, но и смысла в нём нет.
Лучше так:
interface Car {}
class SportsCar implements Car {}
class SuvCar implements Car {}Теперь видно, где абстракция, а где реализация.
Имя говорит о роли, а не о типе.
4. Не нужно дублировать интерфейсы
Многие создают интерфейсы просто ради порядка:
interface IUser {
name: string;
login(): void;
}
class User implements IUser {
name: string;
login() {}
}
В TypeScript это лишнее.
Есть утилитные типы — Required<T>, Omit<T>, Exclude<T> — которые позволяют описывать контракты без дублирования:
class UserService {
getUser() {
return {
name: 'Alice'
};
}
saveUser() {
/* ... */
}
}
class UserServiceMock implements Required<UserService> {
getUser() {
return {
name: 'Mocked Alice'
};
}
saveUser() {
/* ... */
}
}5. Хорошие имена заменяют префиксы
Плохой пример:
class AuthManager implements IAuthManager {}Хороший:
class LocalAuthManager implements AuthManager {}Из второго имени сразу понятно, что делает класс.
Итого
Префикс I:
не несёт пользы;
ломает инкапсуляцию;
провоцирует плохие имена;
создаёт ненужные зависимости.
Современный TypeScript не нуждается в этом наследии.
Пишите просто:
interface User { ... }
class AdminUser implements User { ... }
а не:
interface IUser { ... }
class User implements IUser { ... }
В заключение
Рекомендации TypeScript — не закон, а опыт.
Код и с I скомпилируется, но будет хуже читаться.
Если вы хотите писать чисто и современно,
— оставьте I в прошлом.
Когда в Риме — поступай как римлянин.
Когда в TypeScript — называй вещи своими именами.