Siterem

Создание сайтов под ключ

Пн-Вс: 12:00-22:00 (MSK)
Заказать звонок

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

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

В 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:@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-тегов

Источник: habr.com

Обратный звонок

Оставьте свой телефон и мы свяжемся с Вами

Заявка принята, спасибо!
Мы вскоре свяжемся с Вами ;)