interface vs type: когда что
interfaceиtypeв TypeScript имеют много общего при описании объектных типов, но отличаются по возможностям: interface поддерживает declaration merging иimplements, type alias — union, intersection, conditional и mapped types.
Зачем нужно
Команды часто спорят о выборе между interface и type. Понимание различий помогает принимать осознанные решения и объяснять их, а не следовать произвольным правилам стайлгайда.
Где используется
interface— объектные типы, которые должны расширяться или реализовываться классамиtype— union, примитивы, кортежи, mapped types, conditional types- Оба — для объектных типов (в большинстве случаев взаимозаменяемы)
Основной контент
Что умеют оба
// Оба описывают объектные типы
interface UserI { name: string; age: number }
type UserT = { name: string; age: number };
// Оба расширяются (синтаксис разный)
interface AdminI extends UserI { permissions: string }
type AdminT = UserT & { permissions: string };
// Оба используются как типы параметров
function greet(user: UserI): void { }
function greet(user: UserT): void { }
Что умеет только interface
// 1. Declaration Merging — дополнение из нескольких мест
interface Config { host: string; }
interface Config { port: number; }
// Итог: { host: string; port: number } — type не поддерживает
// 2. Реализация классами (implements)
class Server implements Config {
host = "localhost";
port = 3000;
}
// 3. Рекурсивные объектные типы (без промежуточного alias)
interface TreeNode {
value: number;
children: TreeNode; // OK
}
Что умеет только type
// 1. Union types
type ID = string | number;
type Status = "active" | "inactive" | "pending";
// interface не может: interface ID = string | number // Error
// 2. Кортежи как именованный тип
type Pair = [string, number];
type RGB = [red: number, green: number, blue: number];
// 3. Mapped types
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Nullable<T> = { [K in keyof T]: T[K] | null };
// 4. Conditional types
type IsString<T> = T extends string ? true : false;
// 5. Computed type (вычисляемые через утилиты)
type UserKeys = keyof { name: string; email: string }; // "name" | "email"
Производительность: interface vs type
// interface быстрее при extends цепочках
// TypeScript кэширует interface, пересчитывает type при каждом использовании
// Для объектных типов в сложных цепочках — предпочитайте interface
// type быстрее для простых псевдонимов примитивов и union
type Id = string; // просто псевдоним, нет смысла в interface
Рекомендации
// Используйте interface когда:
// - Это объектный тип, который будут расширять другие
// - Класс будет реализовывать контракт
// - Нужна возможность дополнить тип (declaration merging)
interface Repository<T> {
findById(id: string): Promise<T | null>;
}
// Используйте type когда:
// - Union, intersection, кортеж
// - Mapped, conditional, utility type
// - Псевдоним примитива или функционального типа
type Callback = (err: Error | null, data?: unknown) => void;
type Nullable<T> = T | null;
Частые ошибки
- Пытаться сделать union через interface —
interface Status = "a" | "b"— синтаксическая ошибка; используйтеtype. - Ожидать merging для type — два
type Userвызовут ошибку дублирования; толькоinterfaceподдерживает merging. - Думать что
extendsу type и interface одинаковы —interfaceextends проверяет совместимость и выдаёт ошибку при конфликте;type &молча создаётneverпри несовместимых полях. - Переключать interface/type в середине проекта — выберите конвенцию и придерживайтесь её; смешивание без причины снижает читаемость.
Связанные темы
- interface -- объявление и использование
- type alias -- объявление
- Расширение интерфейсов -- extends
- Intersection Types
- _MOC TypeScript