Главная / Статьи

Роль моста TypeScript 6 на пути к нативному компилятору TS 7

Узнайте, как TypeScript 6 обновляет стандартные настройки, механизм разрешения модулей и синтаксис импорта, чтобы подготовить кодовые базы к более быстрому компилятору TypeScript 7 на основе Go.

11 мин чтения2481 слов

Каждый разработчик, работающий с крупной базой кода на 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}

  
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    Роль моста TypeScript 6 на пути к нативному компилятору TS 7
    
    
    
    
  
  
    
);

Благодаря этому решению можно избежать целого класса ошибок ввода регулярных выражений, не добавляя ни одной зависимости в проект.

Подготовка к детерминированному порядку типов

Ещё одним важным, но менее заметным добавлением в 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, обновление будет заключаться просто в повышении версии пакета и запуске нового нативного компилятора — время сборки сократится до менее чем одной секунды, и не потребуется переписывать существующий код.

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

Каков ваш план по обновлению?

Ваша команда уже работает в режиме строгой проверки, или вы все еще разбираетесь со старыми настройками? Начали ли вы экспериментировать с импортами по подпутям в своих проектах? Поделитесь своими мыслями и вопросами в комментариях.

Роль моста TypeScript 6 на пути к нативному компилятору TS 7

Главная / Статьи

Роль моста TypeScript 6 на пути к нативному компилятору TS 7

Узнайте, как TypeScript 6 обновляет стандартные настройки, механизм разрешения модулей и синтаксис импорта, чтобы подготовить кодовые базы к более быстрому компилятору TypeScript 7 на основе Go.

11 мин чтения2481 слов

Каждый разработчик, работающий с крупной базой кода на 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}

  
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    Роль моста TypeScript 6 на пути к нативному компилятору TS 7
    
    
    
    
  
  
    
);

Благодаря этому решению можно избежать целого класса ошибок ввода регулярных выражений, не добавляя ни одной зависимости в проект.

Подготовка к детерминированному порядку типов

Ещё одним важным, но менее заметным добавлением в 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, обновление будет заключаться просто в повышении версии пакета и запуске нового нативного компилятора — время сборки сократится до менее чем одной секунды, и не потребуется переписывать существующий код.

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

Каков ваш план по обновлению?

Ваша команда уже работает в режиме строгой проверки, или вы все еще разбираетесь со старыми настройками? Начали ли вы экспериментировать с импортами по подпутям в своих проектах? Поделитесь своими мыслями и вопросами в комментариях.

);\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E \u003Cp\u003EБлагодаря этому решению можно избежать целого класса ошибок ввода регулярных выражений, не добавляя ни одной зависимости в проект.\u003C\u002Fp\u003E \u003Ch2\u003EПодготовка к детерминированному порядку типов\u003C\u002Fh2\u003E \u003Cp\u003EЕщё одним важным, но менее заметным добавлением в TypeScript 6 является флаг \u003Ccode\u003EstableTypeOrdering\u003C\u002Fcode\u003E.\u003C\u002Fp\u003E \u003Cp\u003EВ версиях TypeScript до 5.x порядок членов объединённого типа зависел от последовательности, в которой компилятор обрабатывал файлы в памяти. Поскольку компиляция происходила на одной потоке, этот порядок обычно оставался неизменным при каждой компиляции.\u003C\u002Fp\u003E\u003Cp\u003EОднако при переходе на параллелизованный компилятор, какой планируется для TypeScript 7, рабочие потоки могут выполнять свою работу в разном порядке каждый раз. Если один поток заканчивает работу раньше другого, результат объединения может иметь вид \u003Ccode\u003Estring | number\u003C\u002Fcode\u003E; если запустить сборку снова или на другом компьютере, вместо этого может получиться \u003Ccode\u003Enumber | string\u003C\u002Fcode\u003E.\u003C\u002Fp\u003E \u003Cp\u003EЧтобы генерируемые файлы \u003Ccode\u003E.d.ts\u003C\u002Fcode\u003E не менялись непредсказуемо, TypeScript 7 внутри себя применяет строгое, детерминированное правило сортировки. В TypeScript 6 теперь также можно включить это же поведение:\u003C\u002Fp\u003E\u003Cpre\u003E\u003Ccode class=\"language-text\"\u003E\u002F\u002F tsconfig.json\n{\n "compilerOptions": {\n "stableTypeOrdering": true\n }\n}\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E \u003Cp\u003EЕсли ваша работа связана с поддержкой пакетов с открытым исходным кодом или сохранением генерируемых файлов деклараций в системе контроля версий, стоит включить этот флаг и протестировать его сегодня. Это гарантирует, что ваши тесты и генерируемые типы не будут показывать бессмысленные различия при переходе на TypeScript 7.\u003C\u002Fp\u003E \u003Ch2\u003EПрактический чек-лист миграции\u003C\u002Fh2\u003E \u003Cp\u003EПереход на новую версию при значительном объеме кода не обязательно должен быть сложным, если выполнять его поэтапно. Учтите следующие приоритеты:\u003C\u002Fp\u003E\u003Cp\u003EСначала проверьте все флаги, связанные с режимом \u003Ccode\u003Estrict\u003C\u002Fcode\u003E, поскольку это ваш лучший способ защиты от косвенных значений \u003Ccode\u003Eany\u003C\u002Fcode\u003E и неконтролируемых ссылок на \u003Ccode\u003Enull\u003C\u002Fcode\u003E, которые могут проникнуть до выхода версии 7 — высокий приоритет.\nУстаревшие настройки модулей следует устранить, отказавшись от технологий AMD, UMD и устаревшей стратегии разрешения модулей в \u003Ccode\u003Enode\u003C\u002Fcode\u003E — высокий приоритет.\nВключите параметр \u003Ccode\u003EverbatimModuleSyntax\u003C\u002Fcode\u003E, чтобы генерация JavaScript больше не зависела от проверки типов — средний приоритет.\nИспользуйте импорты по подпутям с префиксом \u003Ccode\u003E#\u003C\u002Fcode\u003E, что исключает необходимость в специфических для бандлеров методах разрешения путей — средний приоритет.\nЯвно укажите значение \u003Ccode\u003ErootDir\u003C\u002Fcode\u003E, чтобы избежать неожиданностей в структуре папок вывода при одновременной сборке — низкий приоритет.\u003C\u002Fp\u003E \u003Ch2\u003EРаспространённые ошибки, которых следует избегать\u003C\u002Fh2\u003E \u003Ch2\u003EОшибка 1: отключение режима Strict Mode для устранения ошибок обновления\u003C\u002Fh2\u003E\u003Cp\u003EЧастым рефлексом после обновления до TypeScript 6 и появления множества новых ошибок является простое установление \u003Ccode\u003E"strict": false\u003C\u002Fcode\u003E, чтобы интеграция снова прошла успешно.\u003C\u002Fp\u003E \u003Cp\u003EЭто решение может временно избавить от проблем, но лишь откладывает настоящую работу. Улучшения производительности в TypeScript 7 основаны на предположении корректной типизации. Вместо того чтобы полностью отключать строгий режим, временно отключайте отдельные проверки — например, \u003Ccode\u003E"noImplicitAny": false\u003C\u002Fcode\u003E — и постепенно устраняйте возникающие ошибки, файл за файлом.\u003C\u002Fp\u003E \u003Ch2\u003EОшибка 2: Смешивание импортов типов и значений\u003C\u002Fh2\u003E \u003Cp\u003EПосле включения \u003Ccode\u003EverbatimModuleSyntax\u003C\u002Fcode\u003E не следует объединять импорты типов и значений в одном операторе:\u003C\u002Fp\u003E\u003Cpre\u003E\u003Ccode class=\"language-javascript\"\u003E\u002F\u002F Избегайте\nimport { User, UserService } from ".\u002FuserService";\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E \u003Cpre\u003E\u003Ccode class=\"language-javascript\"\u003E\u002F\u002F Лучше использовать\nimport { UserService } from ".\u002FuserService";\nimport type { User } from ".\u002FuserService";\n\u003C\u002Fcode\u003E\u003C\u002Fpre\u003E \u003Cp\u003EРазделение их позволяет избежать неоднозначности — как для читателей, так и для компилятора — в том, какие импорты предназначены только для проверки типов, а какие содержат реальный код во время выполнения.\u003C\u002Fp\u003E \u003Ch2\u003EОбщая картина: что будет дальше?\u003C\u002Fh2\u003E \u003Cp\u003ETypeScript 6 не стремится предложить вам кучу новой синтаксиса для изучения. Его настоящая цель — подготовить почву к гораздо более значительным изменениям.\u003C\u002Fp\u003E\u003Cp\u003EОбновив сейчас ваш файл \u003Ccode\u003Etsconfig.json\u003C\u002Fcode\u003E, перейдя на импорты с явным указанием типов и устранив старый способ обработки путей, вы избавите себя от подавляющего большинства проблем, с которыми пришлось бы столкнуться позже. Как только выйдет TypeScript 7, обновление будет заключаться просто в повышении версии пакета и запуске нового нативного компилятора — время сборки сократится до менее чем одной секунды, и не потребуется переписывать существующий код.\u003C\u002Fp\u003E \u003Cp\u003EВыделите немного времени на этой неделе, чтобы просмотреть конфигурацию вашего проекта. Это небольшие усилия, которые принесут значительную пользу в дальнейшем.\u003C\u002Fp\u003E \u003Ch2\u003EКаков ваш план по обновлению?\u003C\u002Fh2\u003E \u003Cp\u003EВаша команда уже работает в режиме строгой проверки, или вы все еще разбираетесь со старыми настройками? Начали ли вы экспериментировать с импортами по подпутям в своих проектах? Поделитесь своими мыслями и вопросами в комментариях.\u003C\u002Fp\u003E","showLangNote":false,"schemaTitle":"Роль моста TypeScript 6 на пути к нативному компилятору TS 7","schemaDescription":"Узнайте, как TypeScript 6 обновляет стандартные настройки, механизм разрешения модулей и синтаксис импорта, чтобы подготовить кодовые базы к более быстрому компилятору TypeScript 7 на основе Go.","related":[{"slug":"migrating-express-apis-to-next-js-app-router-route-handlers","title":"Миграция API Express в обработчики маршрутов Next.js App Router"},{"slug":"reranking-must-earn-its-latency","title":"Позвольте механизму переранжирования занять своё место в вашей инфраструктуре RAG."}]}},"actionData":null,"errors":null}