TypeScript 4.5: что нового

Kate

Administrator
Команда форума
Разработчики представили TypeScript 4.5. В новой версии поработали над производительностью языка, добавили новые возможности автодополнения кода для редакторов и упростили способы переподключения библиотек.

87fa8d6db7e5d8619db7d196e965d6f1.jpg

В TypeScript 4.5 представлен новый служебный тип Awaited, который предназначен для моделирования операций await в async-функциях или для рекурсивного развертывания promise-объектов:

// A = string
type A = Awaited<Promise<string>>;

// B = number
type B = Awaited<Promise<Promise<number>>>;

// C = boolean | number
type C = Awaited<boolean | Promise<number>>;
Новый тип Awaited может быть полезен для моделирования существующих API, включая и встроенные модули JavaScript, таки как Promise.all и Promise.race. Причиной создания типа Awaited как раз и стали некоторые проблемы логического вывода Promise.all в прошлых версиях.

Как известно, для нормальной интеграции JavaScript и TypeScript, TypeScript объединяет серии объявлений в файлы типа .d и .ts. Эти файлы объединений предоставляют доступные API-интерфейсы на языке JavaScript и стандартные API-интерфейсы DOM. Использование файлов объединений можно настроить, изменив параметр lib в tsconfig.json. Но у этого метода есть несколько существенных минусов:

  • при обновлении TypeScript необходимо вручную внести изменения в файлы объединений;
  • сами файлы относительно тяжело настроить в соответствии с потребностями проекта.
Теперь в версии 4.5 представлен способ переопределения конкретной встроенной библиотеки аналогичный тому, как работает метод @types. Во время принятия решения о том, какие файлы библиотеки стоит включать, TypeScript будет сначала искать пакеты виды @typescript/lib-* в области видимости node_modules. К примеру, если включить dom в качестве опции lib, то TypeScript будет использовать типы node_modules/@typescript/lib-dom. Конечно же только в том случае, если они доступны для использования.

Затем уже можно обратиться к диспетчеру для того, чтобы установить пакет, который заменит библиотеку. К примеру, вместе с новой версией TypeScript разработчики опубликовали версии API-интерфейсов DOM на @types/web и для привязки проекта к определенной версии DOM-интерфейса достаточно добавить в package.json следующие строчки:

{
"dependencies": {
"@typescript/lib-dom": "npm:mad:types/web"
}
}
После этого можно спокойно обновлять TypeScript и не бояться потерять необходимые зависимости.

Также в TypeScript 4.5 теперь можно сузить значения строк шаблона и установить использовать их в качестве дискриминанта. К примеру, следующий отрывок кода не корректно исполнялся в прошлых версиях, но сейчас спокойно проходит проверку типов:

export interface Success {
type: `${string}Success`;
body: string;
}

export interface Error {
type: `${string}Error`;
message: string;
}

export function handler(r: Success | Error) {
if (r.type === "HttpSuccess") {
// 'r' has type 'Success'
let token = r.body;
}
}
В новой версии реализовали механизм устранения хвостовой рекурсии при вызове условных типов. Известно, что TypeScript прекращает выполнение кода, если обнаруживает бесконечную рекурсию или расширение типов с множественной промежуточной генерацией результатов. За примером обратимся к следующему отрывку кода. В данном случае тип TrimLeft удаляет пробелы в начале строки — если обнаружена строка с пробелом в начале, то оставшаяся часть строки передается обратно в TrimLeft:

type InfiniteBox<T> = { item: InfiniteBox<T> }

type Unpack<T> = T extends { item: infer U } ? Unpack<U> : T;

// error: Type instantiation is excessively deep and possibly infinite.
type Test = Unpack<InfiniteBox<number>>
И все будет работать, но если вдруг строка будет содержать в начале 50 пробелов, то при выполнении выйдет ошибка:

type TrimLeft<T extends string> =
T extends ` ${infer Rest}` ? TrimLeft<Rest> : T;

// error: Type instantiation is excessively deep and possibly infinite.
type Test = TrimLeft<" oops">;
Все дело в том, что TrimLeft написан с использованием хвостовой рекурсии в одной ветке. Когда тип вызывает сам себя, то возвращает результат и ничего с ним не делает. Поскольку таким типам не требуется создавать промежуточные результаты, то их можно реализовать быстрее. Именно поэтому в TypeScript 4.5 устранили хвостовую рекурсию для условных типов — пока одна ветвь условного типа является просто другим условным типом, TypeScript может избегать промежуточные результаты.

В некоторых случаях TypeScript не может точно определить используется ли импорт в коде. К примеру, следующий отрывок кода будет по умолчанию удален:

import { Animal } from "./animal.js";

eval("console.log(new Animal().isDangerous())");
Теперь появился новый флаг --preserveValueImports, который запрещает TypeScript удалять любые пользовательские импортированные значения. Следует обратить внимание на то, что флаг имеет особое требование для использования в сочетании с --isolatedModules — импортируемые типы должны быть обозначены как исключительно типовые.

Также TypeScript 4.5 поддерживает предложение ECMAScript для проверки на факт того, есть ли у объекта приватные поля. Теперь можно создать класс с элементом поля #private и посмотреть, есть ли у другого объекта такое же поле с помощью оператора in:

class Person {
#name: string;
constructor(name: string) {
this.#name = name;
}

equals(other: unknown) {
return other &&
typeof other === "object" &&
#name in other && // <- this is new!
this.#name === other.#name;
}
}
Кроме всех прочих обновлений синтаксиса, TypeScript теперь на всех ОС поддерживает функцию realpathSync.native в Node.js. Раньше эта функция была доступна только в Linux. Теперь же, если у пользователя установлена последняя версия Node.js, то компилятор будет использовать эту функцию в Windows и macOS. Этот шаг помог ускорить загрузку проектов на 5-13%.

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

Автодополнение методов в подклассе
Автодополнение методов в подклассе
Второй тип автодополнения относится к фрагментам JSX-атрибутов. Теперь при записи атрибута в JSX-тег TypeScript предложит несколько готовых вариантов и разместит курсор в нужном месте. Это поможет сэкономить немного времени при наборе кода.

Автодополнение JSX-тегов
Автодополнение JSX-тегов

 
Сверху