Без "I" – только чистый Typescript
Без "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 — называй вещи своими именами.