export default vs export: что выбрать в TypeScript?

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

TypeScript export vs default export: разбираемся раз и навсегда

Привет, друг! Если ты только начинаешь свой путь в TypeScript (или уже не новичок, но всё ещё путаешься в экспортах), то эта статья — твой спасательный круг. Тема "TypeScript export vs default export" — одна из самых горячих на Stack Overflow, и не зря. Кажется, что мелочь, а сколько копий сломано! Давай разложим по полочкам, что к чему, и навсегда запомним, когда использовать export, а когда export default.

Мы не будем сухо перечислять правила. Мы разберём живые примеры, типичные ошибки новичков и дадим конкретные рекомендации. Погнали!

Что такое экспорт в TypeScript и зачем он нужен?

Представь, что ты пишешь большую программу. Ты же не будешь весь код пихать в один файл? Конечно, нет. Ты разбиваешь его на модули: один файл отвечает за работу с пользователями, другой — за расчёты, третий — за интерфейс. Экспорт — это способ сказать TypeScript: "Эй, эту функцию, класс или переменную можно использовать в других файлах". А импорт — это способ сказать: "Дай-ка я возьму эту штуку из другого файла".

В TypeScript есть два основных вида экспорта: именованный (named export) и экспорт по умолчанию (default export). И вот тут начинается самое интересное.

Именованный экспорт (named export) — точность и порядок

Именованный экспорт — это когда ты явно указываешь имя того, что экспортируешь. Это как если бы ты повесил на каждую коробку в своём гараже яркую бирку: "Инструменты", "Спортинвентарь", "Старые учебники".

Синтаксис очень простой. Просто добавь ключевое слово export перед объявлением:

Пример именованного экспорта

// utils.ts
export const PI = 3.14159;

export function calculateArea(radius: number): number {
  return PI * radius * radius;
}

export class Circle {
  constructor(public radius: number) {}
}

А теперь импортируем это в другой файл:

// app.ts
import { PI, calculateArea, Circle } from './utils';

console.log(PI); // 3.14159
const area = calculateArea(5);
const myCircle = new Circle(10);

Обрати внимание на фигурные скобки { } при импорте. Они — верный признак именованного экспорта. Ты можешь импортировать только то, что тебе нужно, и при этом ты обязан использовать те же имена, которые указаны при экспорте (ну, или переименовать их с помощью as, но об этом позже).

Плюсы именованного экспорта

  • Явность: Ты всегда видишь, что именно экспортируется из модуля.
  • Автодополнение: IDE (например, VS Code) отлично подсказывает имена при импорте.
  • Рефакторинг: Если ты переименуешь экспортированную функцию, IDE переименует её во всех местах импорта. Это безопасно.
  • Tree-shaking: Сборщики (Webpack, Vite) могут "вытрясти" неиспользуемый код, уменьшая размер итогового бандла.

Экспорт по умолчанию (default export) — удобство и лаконичность

Экспорт по умолчанию — это когда ты говоришь: "В этом файле есть одна главная сущность, и я хочу, чтобы её было удобно импортировать". Это как если бы ты сказал: "Вот моя визитка, на ней всё основное".

В каждом модуле может быть только один экспорт по умолчанию. Синтаксис:

Пример дефолтного экспорта

// logger.ts
const log = (message: string): void => {
  console.log(`[LOG]: ${message}`);
};

export default log;

Или можно сразу объявить:

export default function log(message: string): void {
  console.log(`[LOG]: ${message}`);
}

Импорт выглядит так (без фигурных скобок!):

// main.ts
import myLog from './logger';

myLog('Hello, world!'); // [LOG]: Hello, world!

Обрати внимание: при импорте мы можем назвать импортируемую штуку как угодно. myLog, logger, logFunction — всё сработает. Это удобно, но может привести к путанице.

Плюсы дефолтного экспорта

  • Лаконичность: Не нужно запоминать точное имя. Импорт короче.
  • Удобно для главной сущности модуля: Например, компонент React или главная функция библиотеки.

Главные отличия и когда что использовать

Теперь, когда мы разобрали оба подхода, давай сравним их в таблице (мысленно, конечно, но я всё распишу).

1. Количество экспортов

  • Именованный: Сколько угодно. Хоть 50 функций и 100 констант.
  • Дефолтный: Только один на модуль.

2. Имя при импорте

  • Именованный: Должно совпадать с именем при экспорте (или использовать as).
  • Дефолтный: Может быть любым. Это и плюс, и минус.

3. Автодополнение в IDE

  • Именованный: Отличное автодополнение. IDE подскажет, что можно импортировать.
  • Дефолтный: Автодополнение хуже. IDE часто не знает, как называется дефолтный экспорт, пока ты его не импортируешь.

4. Рефакторинг (переименование)

  • Именованный: Безопасный рефакторинг. Все импорты обновятся автоматически.
  • Дефолтный: При переименовании экспортируемой сущности импорты не обновятся, так как они используют произвольные имена.

Типичные ошибки новичков (и как их избежать)

Я видел это сотни раз. Давай разберём самые частые грабли.

Ошибка 1: Путаница с синтаксисом импорта

Самая популярная ошибка — попытка импортировать default экспорт в фигурных скобках или наоборот.

// Неправильно!
import { log } from './logger'; // Ошибка! log - это default export, нужны фигурные скобки? Нет!

//                

Блоги

Рекомендаций по книгам