Статья опубликована на английском языке.
TypeScript 6's Bridge Role in the Path to a Native TS 7 Compiler
Learn how TypeScript 6 updates default configs, module resolution, and import syntax to prepare codebases for the faster, Go-based TypeScript 7 compiler.
Каждый разработчик, работающий с крупной базой кода на TypeScript, сталкивается с хорошо знакомой задержкой. После сохранения файла редактор на несколько секунд замирает, пока языковой сервер обрабатывает запрос. В CI-пайплайнах накапливаются pull-запросы, поскольку одна только проверка типов может занять от пяти до десяти минут.
Разработчик сохраняет файл ──> [ tsc на основе JS: однопоточная обработка ] ──> медленная обратная связь
Разработчик сохраняет файл ──> [ Нативный движок TypeScript 7: параллельные потоки ] ──> мгновенная обратная связь
TypeScript 7 вводит нативный компилятор, написанный на Go. Он обрабатывает задачи в нескольких потоках, что сокращает время сборки в 8–10 раз. Однако нельзя просто добавить многопоточный нативный компилятор в проект, который всё ещё зависит от настроек 2018 года.
Вот в чём заключается цель TypeScript 6: он служит переходным этапом. Он устраняет устаревшие технические проблемы, обновляет устаревшие стандартные настройки и гарантирует плавную компиляцию проекта после появления TypeScript 7.
Почему TypeScript нужна версия-мост?
Более десяти лет сам компилятор TypeScript писался на TypeScript и работал под управлением Node.js. Это облегчало разработчикам JavaScript прямое участие в развитии кодовой базы компилятора.
Проблема заключается в том, что выполнение JavaScript происходит в однопоточном режиме. По мере того как кодовые базы превращались в огромные монорепозитории с миллионами строк кода, компилятор в конечном итоге столкнулся с серьёзными ограничениями по производительности.
TypeScript 7 решает эту проблему путём параллельной обработки нативного машинного кода на нескольких ядрах CPU. Однако параллельный компилятор создаёт два новых требования к поведению, которые раньше не имели значения:
- Детерминистичное порядок обработки: Даже когда несколько ядер одновременно анализируют типы, компилятор должен гарантировать, что сообщаемые ошибки и определяемые типы всегда формируются в последовательности, соответствующей определенным правилам и воспроизводимой при повторных запусках.
- Соответствие современным стандартам: Продолжение поддержки устаревших систем модулей, существующих уже десятилетиями, таких как AMD, или устаревших стратегий разрешения проблем добавляет ненужную сложность и нагрузку к natивному компилятору.
В TypeScript 6 команда начала применять эти требования. Она побуждает разработчиков следовать современным конвенциям ECMAScript, чтобы переход на версию 7 не приводил к сбоям.
Самые значительные изменения в конфигурации tsconfig.json
Большинство заметных изменений, внесенных в TypeScript 6, находятся в файле tsconfig.json. Несколько давно существовавших значений по умолчанию были обновлены, чтобы отражать реальные практики сборки проектов в настоящее время.
1. Теперь по умолчанию включен режим strict: true
Ранее пустой файл tsconfig.json означал, что TypeScript работает в более либеральном режиме, если только вы не включили строгий режим вручную с помощью параметра "strict": true.
Начиная с TypeScript 6, строгий режим включается автоматически.
// tsconfig.json
{
"compilerOptions": {
// Этот параметр теперь включен по умолчанию в TypeScript 6
"strict": true
}
}
Если в вашем проекте уже настроено strict: true, ничего не изменится. Но если ваш код использовал косвенные типы any или необработанные значения null и undefined, обновление сразу же выявит ошибки типов.
Почему это важно:
Нативный проверщик типов работает наиболее эффективно, когда информация о типах явна и последовательна. Расслабленный подход к типам заставляет компилятор обрабатывать непредсказуемые сценарии, что замедляет статический анализ.
2. Значение параметра Module Target по умолчанию — esnext
Исторически TypeScript использовал более старые цели компиляции, такие как ES3 или ES5. На практике почти все среды выполнения, применяемые в производстве сегодня, являются «вечнозелеными» — Node.js, Bun, Deno и современные браузеры все поддерживают нативные модули ECMAScript.
В TypeScript 6 параметр module по умолчанию теперь равен esnext, а параметр target указывает на текущую версию ECMAScript.
// Рекомендуемая современная настройка
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler", // или "nodenext"
"target": "es2024">
}
}
Если вашему приложению всё ещё нужен формат вывода CommonJS для поддержки старых серверных сред, вы можете самостоятельно установить "module": "commonjs". TypeScript больше не предполагает использования устаревшего формата вывода, если вы сами этого не запрашиваете.
Чистые границы модулей: прощайте трюки с baseUrl
Раньше было распространено использование алиасов путей в конфигурации вот так:
// Старый шаблон в tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"paths": {
"@components/*": ["src/components/*"],
"@services/*": ["src/services/*"]
}
}
}
В более старых версиях TypeScript для обработки маппингов paths необходимо было наличие поля baseUrl. Это требование создавало проблемы, поскольку baseUrl также позволял разработчикам использовать импорты без относительного префикса — например, import { Button } from "src/components/Button" — что затрудняло различение локальных файлов проекта и пакетов, загружаемых из npm.
В TypeScript 6 эта зависимость устранена: теперь paths могут работать самостоятельно без необходимости указания baseUrl. Кроме того, TypeScript 6 добавляет встроенную поддержку импортов по подпутям в Node.js, при которой используется префикс #.
Использование встроенных импортов по подпутям
Вместо использования специфичных для TypeScript механизмов алиасирования существующие проекты могут опираться на стандартное поле imports внутри package.json:
// package.json
{
"name": "my-app",
"imports": {
"#services/*": "./src/services/*.js",
"#utils/*": "./src/utils/*.js"
}
}
В своих исходных файлах вы обращаетесь к этим подпутям с использованием стандартной синтаксиса #:
// src/api/user.ts
import ‹0› from „#services/database“;
import ‹1› from „#utils/string“;
export function getUser(id: string) {
const user = db.find(id);
return formatName(user.name);
}
Поскольку этот механизм понимается самим Node.js и не требует переписывания на стороне компилятора, обработка файлов происходит значительно быстрее как в TypeScript 6, так и в предстоящей версии TypeScript 7.
Синтаксис модулей в точном виде и импорты только типов
Частой проблемой в кодовых базах, где смешивается код во время выполнения и объявления типов, является то, что импорты, содержащие только информацию о типах, могут случайно рассматриваться как настоящие, исполняемые импорты. Когда вы подключаете интерфейс с помощью обычного оператора import, любой инструмент, читающий этот файл, должен самостоятельно определить, содержит ли импорт реальную логику на JavaScript или исключительно информацию о типах во время компиляции.
TypeScript 6 побуждает команды включать опцию verbatimModuleSyntax:
// tsconfig.json
{
"compilerOptions": {
"verbatimModuleSyntax": true
}
}
При включении этой опции правило становится однозначным: всё, что является исключительно информацией о типах, должно импортироваться с использованием ключевого слова import type.
// РАНЬЕ: Компилятор должен был проверять, содержат ли объекты User и Order код во время выполнения
import { User, Order, calculateTotal } from "./billing";
// ПОСЛЕ: Четкое и предсказуемое поведение как для компиляторов, так и для инструментов сборки
import { calculateTotal } from "./billing";
import type { User, Order } from "./billing";
Почему это важно для TypeScript 7?
Предстоящий многопоточный, параллелизованный компилятор старается обрабатывать файлы изолированно, когда это возможно. Инструкция import type сразу сообщает ему, что в ней нет ничего, что могло бы привести к генерации JavaScript-кода, поэтому он может пропустить открытие и анализ файла ./billing.ts, чтобы выяснить правила генерации кода для текущего файла. Такая небольшая экономия ресурсов в сумме приводит к значительному снижению нагрузки при компиляции крупных кодовых баз.
Новые встроенные удобства языка
Помимо стандартных настроек, TypeScript 6 вводит ряд повседневных удобств, которые облегчают написание распространенных шаблонов кода.
1. Map.getOrInsert() и Map.getOrInsertComputed()
Подумайте, как часто вам приходилось писать подобную повторяющуюся логику поиска в кэше:
// Старый, повторяющийся способ
const userCache = new Map<string, UserProfile>>();
function getProfile(userId: string): UserProfile {
let profile = userCache.get(userId);
if (!profile) {
profile = fetchProfileFromDatabase(userId);
userCache.set(userId, profile);
}
return profile;
}
В TypeScript 6 поддерживается метод getOrInsertComputed, предложенный TC39, который позволяет заменить эту схему на:
// Современный способ в TypeScript 6
const userCache = new Map<string, UserProfile>>();
function getProfile(userId: string): UserProfile {
// Колбэк выполняется только если ключ ещё не существует
return userCache.getOrInsertComputed(userId, () => {
return fetchProfileFromDatabase(userId);
});
}
Эта версия более компактна, избавляет от необходимости в изменяемой переменной-заменителе и обеспечивает точную инференцию типов на протяжении всего кода.
2. Встроенные типы для RegExp.escape()
До сих пор для очистки специальных символов перед их использованием в регулярном выражении приходилось писать собственные вспомогательные функции или подключать сторонние пакеты. Теперь в TypeScript 6 предусмотрены встроенные определения типов для RegExp.escape():
const userInput = "item.value [test]";
// Безопасная обработка экранирования таких символов, как '.', '[' и ']'
const safePattern = RegExp.escape(userInput);
const regex = new RegExp(`^${safePattern}
Operator Software for Solar, BESS & Video | REACTAPP.TOP
TypeScript 6's Bridge Role in the Path to a Native TS 7 Compiler
);
Благодаря этому решению можно избежать целого класса ошибок ввода регулярных выражений, не добавляя ни одной зависимости в проект.
Подготовка к детерминированному порядку типов
Ещё одним важным, но менее заметным добавлением в TypeScript 6 является флаг stableTypeOrdering.
В версиях TypeScript до 5.x порядок членов объединённого типа зависел от последовательности, в которой компилятор обрабатывал файлы в памяти. Поскольку компиляция происходила на одной потоке, этот порядок обычно оставался неизменным при каждой компиляции.
Однако при переходе на параллелизованный компилятор, какой планируется для TypeScript 7, рабочие потоки могут выполнять свою работу в разном порядке каждый раз. Если один поток заканчивает работу раньше другого, результат объединения может иметь вид string | number; если запустить сборку снова или на другом компьютере, вместо этого может получиться number | string.
Чтобы генерируемые файлы .d.ts не менялись непредсказуемо, TypeScript 7 внутри себя применяет строгое, детерминированное правило сортировки. В TypeScript 6 теперь также можно включить это же поведение:
// tsconfig.json
{
"compilerOptions": {
"stableTypeOrdering": true
}
}
Если ваша работа связана с поддержкой пакетов с открытым исходным кодом или сохранением генерируемых файлов деклараций в системе контроля версий, стоит включить этот флаг и протестировать его сегодня. Это гарантирует, что ваши тесты и генерируемые типы не будут показывать бессмысленные различия при переходе на TypeScript 7.
Практический чек-лист миграции
Переход на новую версию при значительном объеме кода не обязательно должен быть сложным, если выполнять его поэтапно. Учтите следующие приоритеты:
Сначала проверьте все флаги, связанные с режимом strict, поскольку это ваш лучший способ защиты от косвенных значений any и неконтролируемых ссылок на null, которые могут проникнуть до выхода версии 7 — высокий приоритет.
Устаревшие настройки модулей следует устранить, отказавшись от технологий AMD, UMD и устаревшей стратегии разрешения модулей в node — высокий приоритет.
Включите параметр verbatimModuleSyntax, чтобы генерация JavaScript больше не зависела от проверки типов — средний приоритет.
Используйте импорты по подпутям с префиксом #, что исключает необходимость в специфических для бандлеров методах разрешения путей — средний приоритет.
Явно укажите значение rootDir, чтобы избежать неожиданностей в структуре папок вывода при одновременной сборке — низкий приоритет.
Распространённые ошибки, которых следует избегать
Ошибка 1: отключение режима Strict Mode для устранения ошибок обновления
Частым рефлексом после обновления до TypeScript 6 и появления множества новых ошибок является простое установление "strict": false, чтобы интеграция снова прошла успешно.
Это решение может временно избавить от проблем, но лишь откладывает настоящую работу. Улучшения производительности в TypeScript 7 основаны на предположении корректной типизации. Вместо того чтобы полностью отключать строгий режим, временно отключайте отдельные проверки — например, "noImplicitAny": false — и постепенно устраняйте возникающие ошибки, файл за файлом.
Ошибка 2: Смешивание импортов типов и значений
После включения verbatimModuleSyntax не следует объединять импорты типов и значений в одном операторе:
// Избегайте
import { User, UserService } from "./userService";
// Лучше использовать
import { UserService } from "./userService";
import type { User } from "./userService";
Разделение их позволяет избежать неоднозначности — как для читателей, так и для компилятора — в том, какие импорты предназначены только для проверки типов, а какие содержат реальный код во время выполнения.
Общая картина: что будет дальше?
TypeScript 6 не стремится предложить вам кучу новой синтаксиса для изучения. Его настоящая цель — подготовить почву к гораздо более значительным изменениям.
Обновив сейчас ваш файл tsconfig.json, перейдя на импорты с явным указанием типов и устранив старый способ обработки путей, вы избавите себя от подавляющего большинства проблем, с которыми пришлось бы столкнуться позже. Как только выйдет TypeScript 7, обновление будет заключаться просто в повышении версии пакета и запуске нового нативного компилятора — время сборки сократится до менее чем одной секунды, и не потребуется переписывать существующий код.
Выделите немного времени на этой неделе, чтобы просмотреть конфигурацию вашего проекта. Это небольшие усилия, которые принесут значительную пользу в дальнейшем.
Каков ваш план по обновлению?
Ваша команда уже работает в режиме строгой проверки, или вы все еще разбираетесь со старыми настройками? Начали ли вы экспериментировать с импортами по подпутям в своих проектах? Поделитесь своими мыслями и вопросами в комментариях.
Связанные статьи
- Миграция API Express в обработчики маршрутов Next.js App Router — Узнайте, как преобразовать маршруты Express, промежуточные компоненты и схемы обработки данных в Next.js App Router с использованием серверных компонентов и учтением аспектов развертывания.
- Как заставить механизм переранжирования занять своё место в вашей пайплайне RAG — Второй этап ранжирования должен добавлять полезную информацию, сохранять доказательства и показывать измеримый прирост по сравнению с базовым вариантом, основанным только на поиске.
Замена Jest на встроенный тестовый движок Node в Node 24 — Пример реальной миграции показывает, как встроенный тестовый движок Node 24 и встроенная поддержка TypeScript сокращают время выполнения тестов в CI, одновременно устраняя четыре зависимости. Сравнение ИИ-агентов Frontier: Astra, Flash, Fable и Mythos — Разбор производительности новейших версий моделей GPT, Gemini и Claude при выполнении реальных задач, связанных с агентными функциями, таких как программирование, просмотр информации и использование инструментов, а не только при выполнении тестовых бенчмарков. Перепись TypeScript 7 на Go: что это значит для безопасности типов в React — Узнайте, как компилятор TypeScript 7 на основе Go ускоряет процесс сборки и улучшает определение генерических типов, устраняя скрытые типы any в хуках React и JSX. Компилятор TypeScript на Go и нативная обработка: руководство по миграции — Узнайте, как компилятор TypeScript на основе Go и нативная обработка в Node.js повлияют на кодовые базы React и Next.js, а также что следует исправить в вашем tsconfig прямо сейчас.