Чаще пользуешься типами или интерфейсами
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Типы vs Интерфейсы в TypeScript
Это частый вопрос на собеседованиях, и ответ показывает понимание различий между type и interface, а также практический опыт в TypeScript. Правильный ответ должен продемонстрировать знание обоих и умение выбрать нужный инструмент.
Мой подход
В большинстве проектов я пользуюсь обоими, но в разных контекстах:
- Интерфейсы (interface) для определения контрактов компонентов и объектов
- Типы (type) для альясов и более сложных типовых конструкций
Интерфейсы (interface)
Интерфейсы используются когда нужно определить структуру объекта, особенно для props компонентов и API контрактов:
// Props компонента
interface ButtonProps {
label: string;
onClick: (event: React.MouseEvent<HTMLButtonElement>) => void;
disabled?: boolean;
variant?: 'primary' | 'secondary';
}
function Button({ label, onClick, disabled = false, variant = 'primary' }: ButtonProps) {
return (
<button onClick={onClick} disabled={disabled} className={`btn btn-${variant}`}>
{label}
</button>
);
}
Преимущества интерфейсов:
- Слияние (merging) — можно расширять интерфейс в разных местах:
interface User {
id: string;
name: string;
}
// Позже в другом файле
interface User {
email: string;
}
// Результат: User имеет id, name и email
- Наследование — очень удобно для иерархий:
interface Entity {
id: string;
createdAt: Date;
updatedAt: Date;
}
interface User extends Entity {
name: string;
email: string;
}
interface Post extends Entity {
title: string;
content: string;
authorId: string;
}
- Читаемость — четкое определение контракта объекта
Типы (type)
Типы используются когда нужна большая гибкость и составные типы:
// Объединение типов (Union)
type Status = 'loading' | 'success' | 'error' | 'idle';
// Пересечение типов (Intersection)
type UserWithTimestamps = User & Timestamps;
// Условные типы (Conditional)
type Flatten<T> = T extends Array<infer U> ? U : T;
// Типы для функций
type AsyncFunction<T> = () => Promise<T>;
// Альясы для примитивов
type UserId = string & { readonly __brand: 'UserId' };
// Типы с функциональной сигнатурой
type ValidationFunction = (value: string) => boolean;
Преимущества типов:
- Гибкость — union, intersection, conditional типы
- Кортежи и массивы:
type Response = [status: number, data: unknown, error: Error | null];
const handleResponse: Response = [200, { id: 1 }, null];
- Сложные типовые операции:
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
type Optional<T> = {
[K in keyof T]?: T[K];
};
Правила выбора
Используй interface когда:
✅ Определяешь props компонента
interface CardProps {
title: string;
children: React.ReactNode;
}
✅ Определяешь API контракт
interface User {
id: string;
name: string;
email: string;
}
✅ Нужна иерархия с наследованием
interface BaseEntity {
id: string;
}
interface Post extends BaseEntity {
title: string;
}
✅ Возможно слияние интерфейсов
interface Window {
myCustomProp?: string;
}
Используй type когда:
✅ Union типы
type Status = 'active' | 'inactive' | 'pending';
type Result = Success | Error;
✅ Условные типы
type Awaited<T> = T extends Promise<infer U> ? U : T;
✅ Кортежи
type Coordinates = [x: number, y: number, z?: number];
✅ Альясы для примитивов
type Email = string;
type Age = number;
✅ Функциональные типы
type Handler<T> = (event: T) => void;
Практические примеры проекта
// components/Question.tsx
// Props компонента - используем interface
interface QuestionProps {
id: string;
title: string;
difficulty: 'easy' | 'medium' | 'hard';
tags: string[];
onAnswer: (questionId: string, answer: string) => Promise<void>;
isLoading?: boolean;
}
// Данные с API - interface
interface Question {
id: string;
title: string;
description: string;
difficulty: string;
tags: string[];
createdAt: Date;
updatedAt: Date;
}
// Type для status - используем type
type QuestionStatus = 'new' | 'answered' | 'skipped' | 'bookmarked';
// Type для результата API - можем использовать оба
type ApiResponse<T> = {
data: T | null;
error: string | null;
status: 'loading' | 'success' | 'error';
};
function QuestionCard({
id,
title,
difficulty,
onAnswer,
isLoading = false,
}: QuestionProps) {
const [status, setStatus] = React.useState<QuestionStatus>('new');
const handleSubmit = async (answer: string) => {
try {
setStatus('answered');
await onAnswer(id, answer);
} catch (error) {
setStatus('new');
}
};
return (
<article>
<h2>{title}</h2>
<span className={`difficulty difficulty-${difficulty}`}>
{difficulty}
</span>
{/* ... остальной код */}
</article>
);
}
Стилевые соглашения
В современном TypeScript сообществе есть тренд:
- Используй interface по умолчанию для объектов
- Используй type для всего остального
Это согласовано с документацией TypeScript и используется в большинстве крупных проектов (React, Vue, Angular).
// Правильно
interface Props { ... }
type Status = 'active' | 'inactive';
type Handler = () => void;
// Избегай
type Props = { ... }; // Используй interface
interface Status { ... } // Используй type
Производительность
Долгое время верили, что interface быстрее type, но это больше не актуально. TypeScript 5.0+ имеет одинаковую производительность для обоих.
Итоги
В моей практике:
- interface для структур данных и контрактов (props, API, модели)
- type для всего остального (union, conditional, functional)
- Оба используются в проекте, но в разных контекстах
- Главное — быть консистентным и следовать соглашениям команды
- Не переживай по производительности — выбирай по удобству