Pull to refresh

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

Reading time4 min
Views9.1K

Разработчики представили 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-тегов
Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
+6
Comments13

Other news