Loader ts и tsx

В системе сборки Esbuild обработка TypeScript реализована через встроенные загрузчики, которые определяют, как именно интерпретируется файл на этапе трансформации. Лоадеры ts и tsx отвечают за разные сценарии компиляции TypeScript-кода, различаясь поддержкой JSX-синтаксиса и внутренними правилами парсинга.

Роль загрузчиков в архитектуре Esbuild

Каждый входной файл в Esbuild обрабатывается в соответствии с типом загрузчика (loader). Загрузчик определяет:

  • способ синтаксического анализа файла;
  • необходимость трансформации TypeScript → JavaScript;
  • поддержку дополнительных синтаксических расширений (JSX, JSON, CSS и др.).

Для TypeScript используются два основных варианта:

  • ts — чистый TypeScript без JSX;
  • tsx — TypeScript с поддержкой JSX.

Разделение обусловлено тем, что JSX требует отдельного этапа разбора, отличающегося от стандартного TypeScript-парсинга.


Loader ts: обработка чистого TypeScript

Загрузчик ts применяется к файлам .ts, в которых отсутствует JSX-разметка.

Характеристики обработки

При использовании loader: 'ts' Esbuild выполняет следующие шаги:

  • удаление типов (type erasure);
  • преобразование TypeScript-синтаксиса в JavaScript;
  • сохранение структуры модулей (ESM или CJS в зависимости от конфигурации);
  • игнорирование декларативных конструкций TypeScript (interface, type, enum в зависимости от режима компиляции).

Пример входного кода

function sum(a: number, b: number): number {
  return a + b;
}

const value: string = "test";

Результат трансформации

function sum(a, b) {
  return a + b;
}

const value = "test";

Типизация полностью удаляется, при этом логика кода остаётся неизменной.


Loader tsx: обработка TypeScript с JSX

Загрузчик tsx предназначен для файлов .tsx, содержащих JSX-выражения. Он расширяет возможности ts дополнительным этапом разбора JSX-дерева.

Особенности синтаксического анализа

При активации loader: 'tsx' Esbuild:

  • распознаёт JSX-теги внутри TypeScript-кода;
  • преобразует JSX в JavaScript-вызовы функций;
  • продолжает удалять TypeScript-типы аналогично ts-лоадеру;
  • поддерживает различные JSX-режимы (automatic, transform).

Пример входного кода

type Props = {
  title: string;
};

const Component = ({ title }: Props) => {
  return <h1>{title}</h1>;
};

Результат трансформации (classic runtime)

const Component = ({ title }) => {
  return React.createElement("h1", null, title);
};

Различие между ts и tsx

Основное различие заключается не в обработке TypeScript, а в наличии JSX-синтаксиса.

Характеристика ts tsx
Поддержка TypeScript да да
Поддержка JSX нет да
Основное назначение бизнес-логика, утилиты React-компоненты, UI
Результат трансформации JSX отсутствует React/JSX runtime

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


Обработка JSX в tsx-файлах

Esbuild поддерживает два основных режима трансформации JSX:

Automatic runtime

В этом режиме JSX преобразуется без явного использования React.createElement.

const App = () => <div>Hello</div>;

Результат:

import { jsx as _jsx } from "react/jsx-runtime";

const App = () => _jsx("div", { children: "Hello" });

Classic runtime

Используется при старой модели React.

const App = () => <div>Hello</div>;

Результат:

const App = () => React.createElement("div", null, "Hello");

Выбор режима определяется настройкой jsx в конфигурации сборки.


Взаимодействие с TypeScript-конфигурацией

Esbuild не выполняет полноценную типовую проверку TypeScript. Работа ts и tsx ограничивается трансформацией синтаксиса.

Не обрабатываются:

  • строгая проверка типов;
  • диагностика несовместимостей типов;
  • выполнение tsc-пайплайна.

При этом учитываются некоторые опции tsconfig.json, влияющие на трансформацию:

  • target — определяет версию ECMAScript;
  • jsx — задаёт стратегию JSX-трансформации;
  • module — формат модулей;
  • jsxImportSource — источник JSX runtime.

Поведение при импортах и расширениях

Esbuild определяет загрузчик на основе расширения файла:

  • .tsts
  • .tsxtsx

При явном указании loader в конфигурации приоритет имеет пользовательская настройка:

loader: {
  ".ts": "ts",
  ".tsx": "tsx"
}

Особенности резолвинга

  • JSX допускается только в .tsx;
  • generic-синтаксис TypeScript корректно работает только в .ts, если отсутствует JSX;
  • смешивание JSX в .ts приводит к ошибкам парсинга.

Ограничения загрузчиков

Несмотря на высокую скорость, оба загрузчика имеют функциональные ограничения:

Отсутствие type-checking

Esbuild не выполняет проверку типов. Ошибки вроде:

const a: number = "string";

не вызывают остановку сборки.

Ограниченная поддержка TypeScript-специфичных возможностей

Частично поддерживаются:

  • enums (транспиляция в объект);
  • namespaces (в ограниченном виде);
  • experimental decorators (при включении соответствующей опции).

JSX-ограничения

  • отсутствие runtime-валидации JSX;
  • трансформация без React reconciliation логики;
  • зависимость от выбранного runtime (react/jsx-runtime или React.createElement).

Производительность обработки

Разделение ts и tsx позволяет оптимизировать пайплайн:

  • ts обрабатывается быстрее за счёт отсутствия JSX-парсинга;
  • tsx требует дополнительного AST-анализа JSX-узлов;
  • оба варианта используют единый быстрый парсер Esbuild, написанный на Go.

На больших проектах разница становится заметной при массовой обработке UI-компонентов.


Практические сценарии использования

Backend-код на TypeScript

Используется ts:

  • API-сервисы;
  • утилиты;
  • бизнес-логика без UI.

Frontend-компоненты

Используется tsx:

  • React-компоненты;
  • UI-библиотеки;
  • страницы приложений.

Общие ошибки конфигурации

  • применение ts к JSX-файлам;
  • использование tsx для файлов без JSX в больших проектах (не критично, но менее оптимально);
  • отсутствие синхронизации jsx-настроек с React-версией.

Взаимодействие с плагинами Esbuild

Загрузчики ts и tsx могут модифицироваться через плагины:

  • перехват onLoad;
  • кастомная трансформация AST;
  • интеграция с дополнительными препроцессорами.

Плагины могут переопределять поведение загрузчика, например:

  • добавлять макросы;
  • внедрять автоматические импорты;
  • изменять JSX перед трансформацией.

Особенности диагностики ошибок

Ошибки парсинга различаются в зависимости от загрузчика:

ts

  • ошибки generic-синтаксиса;
  • проблемы типов в сложных выражениях.

tsx

  • ошибки JSX-структуры;
  • некорректные закрывающие теги;
  • конфликт JSX и TypeScript generic (<T> vs <div>).

Типичная проблема:

const x = <T>(value: T) => value;

Интерпретируется как JSX, если контекст неясен, что требует корректного выбора загрузчика или синтаксического уточнения.


Резюме поведения трансформации

  • TypeScript полностью компилируется в JavaScript без проверки типов;
  • JSX преобразуется только в tsx;
  • выбор загрузчика влияет на весь синтаксический разбор файла;
  • производительность достигается за счёт минимального AST и отсутствия типового анализа.