TSX представляет собой синтаксическое расширение TypeScript, объединяющее строгую типизацию и JSX-разметку. В рамках SWC (Speedy Web Compiler) обработка TSX строится как многоэтапный конвейер, где исходный код последовательно проходит стадии парсинга, нормализации AST, трансформации типов и JSX-узлов, а затем генерации JavaScript.
Ключевой принцип обработки заключается в разделении ответственности:
SWC реализует это через высокопроизводительный Rust-бэкенд, минимизируя накладные расходы на синтаксический разбор и трансформацию.
На этапе парсинга SWC использует строгий грамматический разбор, который должен одновременно учитывать правила TypeScript и JSX. Это создаёт специфическую сложность: одинаковые конструкции могут интерпретироваться по-разному в зависимости от контекста.
Пример конфликтного синтаксиса:
const value = <T>(arg: T) => arg;
В данном случае <T> может быть:
SWC решает подобные неоднозначности через контекстный парсер, который анализирует предшествующие токены и состояние грамматики.
После парсинга формируется AST, содержащий:
SWC не смешивает семантику TypeScript и JSX в процессе трансформации. Вместо этого используется модель двух независимых проходов:
На этом этапе из AST удаляются:
Пример трансформации:
function sum(a: number, b: number): number {
return a + b;
}
После удаления типов:
function sum(a, b) {
return a + b;
}
Важно, что SWC не выполняет типовую проверку — он работает исключительно как транспилятор.
JSX-узлы преобразуются в вызовы функции React.createElement
или автоматический runtime (jsx, jsxs,
Fragment), в зависимости от конфигурации.
Пример исходного TSX:
const element = <div className="box">Hello</div>;
После трансформации:
const element = React.createElement(
"div",
{ className: "box" },
"Hello"
);
При использовании automatic runtime:
import { jsx as _jsx } from "react/jsx-runtime";
const element = _jsx("div", {
className: "box",
children: "Hello"
});
Обработка TSX активируется через конфигурацию .swcrc.
Базовая структура:
{
"jsc": {
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic",
"importSource": "react"
}
}
}
}
syntax: “typescript” — включает TypeScript-парсер
tsx: true — активирует JSX внутри TypeScript
runtime:
“classic” — React.createElement
“automatic” — новый JSX runtime
importSource — источник JSX runtime функций
Одна из наиболее сложных задач TSX — различение JSX-элементов и generic-параметров.
Рассмотрим пример:
const result = <T extends unknown>(value: T): T => value;
SWC применяет следующие правила:
< следует идентификатор и ключевое слово
extends, это TypeScript generic
<div>), это JSX
Дополнительный пример:
const x = <A, B>(a: A, b: B) => [a, b];
Это интерпретируется как generic-функция, а не JSX.
Fragment в TSX:
const node = (
<>
<span>A</span>
<span>B</span>
</>
);
После трансформации:
import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
const node = _jsx(_Fragment, {
children: [
_jsx("span", { children: "A" }),
_jsx("span", { children: "B" })
]
});
SWC оптимизирует структуру children, превращая последовательные элементы в массив при необходимости.
SWC выполняет несколько оптимизаций, влияющих на итоговый код:
Статические JSX-узлы могут выноситься из функций:
const header = <h1>Title</h1>;
function App() { return <div>{header}</div>; }
Преобразуется в:
const header = /*
- Слияние соседних текстовых узлов
<div>{"A"}{"B"}</div>
Превращается в:
_jsx("div", { children: "AB" });
- Минимизация props-объектов
Если JSX-элемент не имеет props, объект может быть опущен или упрощён.
TypeScript-only конструкции внутри TSX
SWC должен корректно удалять конструкции, которые не должны попадать в
runtime:
Интерфейсы
interface User {
name: string;
}
Полностью исчезают из итогового кода.
Type assertions
const value = someVar as string;
Преобразуется в:
const value = someVar;
Enum
enum Color {
Red,
Blue
}
Преобразуется в JS-объект:
var Color;
(function (Color) {
Color[Color["Red"] = 0] = "Red";
Color[Color["Blue"] = 1] = "Blue";
})(Color || (Color = {}));
Работа с JSX runtime и импортами
SWC автоматически управляет импортами JSX runtime:
-
добавляет
jsx, jsxs, Fragment
-
удаляет лишние импорты React при automatic runtime
-
избегает дублирования импортов
Пример:
const a = <div />;
Результат:
import { jsx as _jsx } from "react/jsx-runtime";
const a = _jsx("div", {});
Если используется classic runtime:
const a = React.createElement("div", null);
Обработка вложенных TSX-структур
TSX допускает сложные вложенные конструкции, где TypeScript и JSX
пересекаются:
function render<T>(items: T[]) {
return (
<ul>
{items.map((item: T) => (
<li>{String(item)}</li>
))}
</ul>
);
}
SWC выполняет:
-
удаление
: T из параметров
-
преобразование JSX в вызовы runtime
-
сохранение структуры map-выражений как JS
Результат:
function render(items) {
return _jsx("ul", {
children: items.map((item) =>
_jsx("li", {
children: String(item)
})
)
});
}
Ошибкоустойчивость парсера TSX
SWC реализует tolerant parsing, позволяющий продолжать трансформацию при
частично некорректном коде. Это особенно важно для редакторных сценариев
и incremental compilation.
Характеристики:
-
восстановление AST при синтаксических ошибках
-
локализация ошибки без остановки пайплайна
-
сохранение максимально возможного объёма трансформации
Влияние target и module на TSX-компиляцию
Поведение TSX-трансформации зависит от целевой платформы:
-
target: es5 — добавляет полифиллы и расширенную
трансформацию
-
target: esnext — минимальная трансформация JSX
-
module: commonjs — преобразует import/export в require
Пример различий:
ESM:
import { jsx as _jsx } from "react/jsx-runtime";
CommonJS:
const { jsx: _jsx } = require("react/jsx-runtime");
Производительность обработки TSX в SWC
SWC оптимизирует TSX через:
-
многопоточную обработку (Rust rayon)
-
zero-copy parsing
-
минимизацию промежуточных AST-структур
-
кеширование parser state
В сравнении с классическими транспайлерами достигается значительное
ускорение обработки крупных TSX-кодов, особенно в монорепозиториях.
Особенности интеграции SWC с React и TypeScript toolchain
SWC часто используется как замена Babel и tsc в связке:
-
Next.js
-
Vite (через плагины)
-
Webpack loaders
Типичная конфигурация loader:
module: {
rules: [
{
test: /\.(ts|tsx)$/,
use: {
loader: "swc-loader",
options: {
jsc: {
parser: {
syntax: "typescript",
tsx: true
}
}
}
}
}
]
}
SWC в таких сценариях берёт на себя исключительно трансформацию,
оставляя типизацию внешним инструментам при необходимости.