Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI29 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Type vs Interface в TypeScript
Это один из самых часто задаваемых вопросов для TypeScript разработчиков. Вроде бы похоже, но есть важные отличия.
Краткий ответ
Эмпирическое правило:
- interface — для object shapes (структур данных)
- type — для всего остального (union, tuple, primitive, function)
Основные отличия
1. Синтаксис и базовое использование
Interface:
interface User {
name: string;
email: string;
age: number;
}
// Используется как обычно
const user: User = {
name: 'John',
email: 'john@example.com',
age: 30
};
Type:
type User = {
name: string;
email: string;
age: number;
};
// Точно такой же синтаксис
const user: User = {
name: 'John',
email: 'john@example.com',
age: 30
};
Результат: одинаково работают!
2. Union types — ВОТ где type нужен
// ❌ interface НЕЛЬЗЯ использовать для union
interface Result = 'success' | 'error' | 'pending'; // ERROR!
// ✅ type используется для union
type Result = 'success' | 'error' | 'pending';
const status: Result = 'success'; // ✅
const status2: Result = 'unknown'; // ❌ Type error
Практический пример:
// API Response может быть одного из нескольких видов
type ApiResponse =
| { status: 'success'; data: User }
| { status: 'error'; message: string }
| { status: 'loading' };
// interface это не может выразить
// interface = всегда объект с фиксированной структурой
3. Tuple types — type только
// ❌ interface для tuple неудобен
interface Pair {
0: string;
1: number;
}
// ✅ type для tuple естественен
type Pair = [string, number];
const pair: Pair = ['hello', 42]; // ✅
4. Функции — type более удобен
// interface для функций
interface Greeter {
(name: string): string;
}
const greet: Greeter = (name) => `Hello, ${name}`;
// type для функций (более понятно)
type Greeter = (name: string) => string;
const greet: Greeter = (name) => `Hello, ${name}`;
5. Declaration Merging — interface только
// ✅ interface можно объединить
interface User {
name: string;
}
interface User {
email: string;
}
// Автоматически merged
const user: User = {
name: 'John',
email: 'john@example.com'
};
// ❌ type так не может
type User = { name: string };
type User = { email: string }; // ERROR: Duplicate type alias
Когда это полезно:
// Расширение библиотеки
declare global {
interface Window {
myCustomProperty: string;
}
}
// Теперь window.myCustomProperty работает везде
6. Intersection types
// interface - расширяет
interface Named {
name: string;
}
interface Aged {
age: number;
}
interface Person extends Named, Aged {} // ✅
// type - пересекает
type Named = { name: string };
type Aged = { age: number };
type Person = Named & Aged; // ✅
// Результат одинаковый
const person: Person = {
name: 'John',
age: 30
};
7. Primitive types — type только
// ❌ interface для primitives
interface Str extends string {} // Технически можно, но странно
// ✅ type для primitives
type Str = string;
type Num = number;
type Bool = boolean;
const str: Str = 'hello';
const num: Num = 42;
8. Conditional types — type только
// Это advanced feature, но показывает мощь type
type IsString<T> = T extends string ? true : false;
type A = IsString<'hello'>; // true
type B = IsString<123>; // false
Полное сравнение в таблице
| Фича | Interface | Type |
|---|---|---|
| Object shapes | ✅ отлично | ✅ отлично |
| Union types | ❌ нельзя | ✅ отлично |
| Tuple types | ❌ неудобно | ✅ отлично |
| Функции | ✅ нормально | ✅ лучше |
| Primitives | ❌ нельзя | ✅ отлично |
| Declaration merge | ✅ да | ❌ нет |
| Extends/Intersection | ✅ extends | ✅ & |
| Conditional types | ❌ нет | ✅ да |
| Performance | Немного лучше | Немного хуже |
| Serialization | Ясно что это интерфейс | Может быть что угодно |
Мой стиль кодирования: Pragmatic Rules
// Правило 1: Объекты для interface
interface User {
id: string;
name: string;
email: string;
}
interface Post {
id: string;
title: string;
author: User;
}
// Правило 2: Всё остальное type
type UserId = string & { readonly __brand: 'UserId' }; // Branded type
type ApiResponse =
| { status: 'success'; data: User }
| { status: 'error'; message: string };
type Handler = (req: Request) => Promise<Response>;
type Nullable<T> = T | null; // Utility type
Практические примеры
Когда type:
// 1. Union types для состояния
type State = 'idle' | 'loading' | 'success' | 'error';
// 2. Результат операции
type Result<T> =
| { ok: true; data: T }
| { ok: false; error: Error };
// 3. Event типы
type Event =
| { type: 'USER_CREATED'; userId: string }
| { type: 'USER_DELETED'; userId: string }
| { type: 'POST_PUBLISHED'; postId: string };
// 4. Utilities
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type Partial<T> = { [P in keyof T]?: T[P] };
// 5. Функции
type Middleware = (req: Request, res: Response, next: () => void) => void;
Когда interface:
// 1. API Response shapes
interface ApiUser {
id: string;
name: string;
email: string;
}
// 2. Config объекты
interface AppConfig {
database: DatabaseConfig;
server: ServerConfig;
}
// 3. Классы
interface Logger {
info(message: string): void;
error(message: string): void;
}
class ConsoleLogger implements Logger {
info(message: string) { console.log(message); }
error(message: string) { console.error(message); }
}
// 4. ORM модели
interface User {
id: string;
name: string;
createdAt: Date;
}
Смешивание interface и type
// Можно комбинировать
interface BaseUser {
id: string;
name: string;
}
type AdminUser = BaseUser & { role: 'admin' };
type GuestUser = BaseUser & { role: 'guest' };
type User = AdminUser | GuestUser;
// Или наоборот
type BaseConfig = { host: string; port: number };
interface AppConfig extends BaseConfig {
database: string;
}
Performance note
// Interface немного быстрее для типчекинга
// Потому что compiler знает это object shape
// Type требует больше вычислений для unions и conditionals
// На практике:
// - Для большых проектов разница < 100ms
// - Readability > performance здесь
Финальный совет
// Просто правило которое я использую:
// 1. Interface для данных (что вернет API, БД)
interface User { ... }
interface Post { ... }
interface Comment { ... }
// 2. Type для всего остального
type UserId = string & { readonly __brand: 'UserId' };
type ApiResponse = { success: true } | { success: false };
type Middleware = (req: Request) => Promise<Response>;
type Optional<T> = T | undefined;
// 3. Это делает код предсказуемым
// - Если вижу interface -> это object shape
// - Если вижу type -> это может быть что угодно
Когда выбирать что
Выбери interface если:
- Моделируешь объект (User, Post, Config)
- Нужен declaration merging
- Работаешь с классом (implements)
Выбери type если:
- Union types нужны
- Tuple/Primitive types
- Utility types
- Функции
- Есть сомнения (type более гибкий)
Правило в большинстве проектов:
70% interface для данных
30% type для логики
В конце концов, оба работают. Но выбор один или другого делает код более читаемым и мейнтейнабильным.