Что нового в TypeScript 5.0: деклараторы, константный тип, улучшение перечислений, скорость и многое другое!

Опубликовано: 2023-04-15

TypeScript 5.0 был официально выпущен 16 марта 2023 года и теперь доступен для всех. В этом выпуске представлено множество новых функций, призванных сделать TypeScript меньше, проще и быстрее.

Этот новый выпуск модернизирует декораторы для настройки классов, позволяя повторно использовать настройку классов и их членов. Разработчики теперь могут добавлять модификатор const к объявлению параметра типа, позволяя использовать вывод, подобный константе, по умолчанию. В новом выпуске все перечисления объединены в перечисления, что упрощает структуру кода и ускоряет работу с TypeScript.

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

Начало работы с TypeScript 5.0

TypeScript — это официальный компилятор, который вы можете установить в свой проект с помощью npm. Если вы хотите начать использовать TypeScript 5.0 в своем проекте, вы можете запустить следующую команду в каталоге вашего проекта:

 npm install -D typescript

Это установит компилятор в каталог node_modules , который теперь можно запустить с помощью команды npx tsc .

В этой документации также можно найти инструкции по использованию новой версии TypeScript в Visual Studio Code.

ICYMI: TypeScript 5.0 уже здесь! Изучите его захватывающие обновления, такие как Declarators, Const Type и улучшенные Enums, в этом руководстве Click to Tweet

Что нового в TypeScript 5.0?

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

Модернизированные декораторы

Декораторы некоторое время были в TypeScript под экспериментальным флагом, но новый выпуск приводит их в соответствие с предложением ECMAScript, которое сейчас находится на стадии 3, то есть на стадии добавления в TypeScript.

Декораторы — это способ настроить поведение классов и их членов повторно используемым способом. Например, если у вас есть класс с двумя методами, greet и getAge :

 class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log(`Hello, my name is ${this.name}.`); } getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();

В реальных случаях использования этот класс должен иметь более сложные методы, которые обрабатывают некоторую асинхронную логику и имеют побочные эффекты и т. д., где вы могли бы добавить некоторые вызовы console.log для помощи в отладке методов.

 class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } greet() { console.log('LOG: Method Execution Starts.'); console.log(`Hello, my name is ${this.name}.`); console.log('LOG: Method Execution Ends.'); } getAge() { console.log('LOG: Method Execution Starts.'); console.log(`I am ${this.age} years old.`); console.log('Method Execution Ends.'); } } const p = new Person('Ron', 30); p.greet(); p.getAge();

Это часто встречающийся шаблон, и было бы удобно иметь решение, применимое к каждому методу.

Здесь в игру вступают декораторы. Мы можем определить функцию с именем debugMethod , которая выглядит следующим образом:

 function debugMethod(originalMethod: any, context: any) { function replacementMethod(this: any, ...args: any[]) { console.log('Method Execution Starts.'); const result = originalMethod.call(this, ...args); console.log('Method Execution Ends.'); return result; } return replacementMethod; }

В приведенном выше коде debugMethod принимает исходный метод ( originalMethod ) и возвращает функцию, которая делает следующее:

  1. Регистрирует сообщение «Выполнение метода начинается».
  2. Передает исходный метод и все его аргументы (включая этот).
  3. Регистрирует сообщение «Выполнение метода завершается».
  4. Возвращает все, что было возвращено исходным методом.

Используя декораторы, вы можете применить debugMethod к своим методам, как показано в коде ниже:

 class Person { name: string; age: number; constructor(name: string, age: number) { this.name = name; this.age = age; } @debugMethod greet() { console.log(`Hello, my name is ${this.name}.`); } @debugMethod getAge() { console.log(`I am ${this.age} years old.`); } } const p = new Person('Ron', 30); p.greet(); p.getAge();

Это выведет следующее:

 LOG: Entering method. Hello, my name is Ron. LOG: Exiting method. LOG: Entering method. I am 30 years old. LOG: Exiting method.

При определении функции декоратора ( debugMethod ) передается второй параметр, называемый context (это объект контекста — содержит некоторую полезную информацию о том, как был объявлен декорированный метод, а также имя метода). Вы можете обновить свой debugMethod , чтобы получить имя метода из объекта context :

 function debugMethod( originalMethod: any, context: ClassMethodDecoratorContext ) { const methodName = String(context.name); function replacementMethod(this: any, ...args: any[]) { console.log(`'${methodName}' Execution Starts.`); const result = originalMethod.call(this, ...args); console.log(`'${methodName}' Execution Ends.`); return result; } return replacementMethod; }

Когда вы запускаете свой код, вывод теперь будет содержать имя каждого метода, украшенного декоратором debugMethod :

 'greet' Execution Starts. Hello, my name is Ron. 'greet' Execution Ends. 'getAge' Execution Starts. I am 30 years old. 'getAge' Execution Ends.

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

Введение в параметры типа const

Это еще один большой релиз, который дает вам новый инструмент с дженериками, чтобы улучшить вывод, который вы получаете, когда вы вызываете функции. По умолчанию, когда вы объявляете значения с помощью const , TypeScript выводит тип, а не его литеральные значения:

 // Inferred type: string[] const names = ['John', 'Jake', 'Jack'];

До сих пор для достижения желаемого вывода приходилось использовать утверждение const, добавляя «as const»:

 // Inferred type: readonly ["John", "Jake", "Jack"] const names = ['John', 'Jake', 'Jack'] as const;

Когда вы вызываете функции, это похоже. В приведенном ниже коде предполагаемый тип странstring[] :

 type HasCountries = { countries: readonly string[] }; function getCountriesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: string[] const countries = getCountriesExactly({ countries: ['USA', 'Canada', 'India'] });

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

 // Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] } as const);

Это может быть трудно запомнить и реализовать. Однако TypeScript 5.0 представляет новую функцию, позволяющую добавить модификатор const к объявлению параметра типа, который автоматически применит вывод, подобный константе, по умолчанию.

 type HasCountries = { countries: readonly string[] }; function getNamesExactly(arg: T): T['countries'] { return arg.countries; } // Inferred type: readonly ["USA", "Canada", "India"] const names = getNamesExactly({ countries: ['USA', 'Canada', 'India'] });

Использование параметров const типа позволяет разработчикам более четко выражать намерения в своем коде. Если предполагается, что переменная должна быть постоянной и никогда не изменяться, использование параметра типа const гарантирует, что она никогда не будет изменена случайно.

Вы можете проверить исходный запрос на вытягивание для получения дополнительной информации о том, как параметр const type работает в TypeScript.

Улучшения перечислений

Перечисления в TypeScript — это мощная конструкция, которая позволяет разработчикам определять набор именованных констант. В TypeScript 5.0 были внесены улучшения в перечисления, чтобы сделать их еще более гибкими и полезными.

Например, если в функцию передано следующее перечисление:

 enum Color { Red, Green, Blue, } function getColorName(colorLevel: Color) { return colorLevel; } console.log(getColorName(1));

До появления TypeScript 5.0 вы могли передать неправильный номер уровня, и это не вызывало ошибки. Но с введением TypeScript 5.0 сразу выдает ошибку.

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

 enum Color { Red, Purple, Orange, Green, Blue, Black, White, } type PrimaryColor = Color.Red | Color.Green | Color.Blue; function isPrimaryColor(c: Color): c is PrimaryColor { return c === Color.Red || c === Color.Green || c === Color.Blue; } console.log(isPrimaryColor(Color.White)); // Outputs: false console.log(isPrimaryColor(Color.Red)); // Outputs: true

Улучшения производительности TypeScript 5.0

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

Например, разница между размером пакета TypeScript 5.0 и 4.9 весьма внушительна.

Недавно TypeScript был перенесен из пространств имен в модули, что позволило ему использовать современные инструменты сборки, которые могут выполнять оптимизацию, например, поднимать область видимости. Кроме того, удаление некоторого устаревшего кода сократило примерно 26,4 МБ по сравнению с 63,8 МБ размера пакета TypeScript 4.9.

Размер пакета TypeScript
Размер пакета TypeScript

Вот еще несколько интересных побед в скорости и размере между TypeScript 5.0 и 4.9:

Сценарий Время или размер относительно TS 4.9
время сборки материала-интерфейса 90%
Время запуска компилятора TypeScript 89%
Время построения драматурга 88%
Время самостоятельной сборки компилятора TypeScript 87%
Время сборки Outlook Web 82%
Время сборки кода VS 80%
typescript npm Размер пакета 59%

Разрешение бандлера для лучшего разрешения модуля

Когда вы пишете оператор импорта на TypeScript, компилятору необходимо знать, на что ссылается импорт. Он делает это, используя разрешение модуля. Например, когда вы пишете import { a } from "moduleA" , компилятору необходимо знать определение a в moduleA , чтобы проверить его использование.

В TypeScript 4.7 были добавлены две новые опции для параметров --module и moduleResolution : node16 и nodenext .

Целью этих параметров было более точное представление точных правил поиска для модулей ECMAScript в Node.js. Однако у этого режима есть несколько ограничений, которые не применяются другими инструментами.

Например, в модуле ECMAScript в Node.js любой относительный импорт должен включать расширение файла, чтобы он работал правильно:

 import * as utils from "./utils"; // Wrong import * as utils from "./utils.mjs"; // Correct

TypeScript представил новую стратегию под названием «упаковщик moduleResolution». Эту стратегию можно реализовать, добавив следующий код в раздел «compilerOptions» вашего файла конфигурации TypeScript:

 { "compilerOptions": { "target": "esnext", "moduleResolution": "bundler" } }

Эта новая стратегия подходит для тех, кто использует современные сборщики, такие как Vite, esbuild, swc, Webpack, Parcel и другие, использующие гибридную стратегию поиска.

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

Устаревшие

TypeScript 5.0 поставляется с некоторыми изменениями, включая требования времени выполнения, изменения lib.d.ts и критические изменения API.

  1. Требования к среде выполнения: TypeScript теперь нацелен на ECMAScript 2018, а пакет устанавливает минимальное ожидание ядра 12.20. Таким образом, пользователи Node.js должны иметь минимальную версию 12.20 или более позднюю, чтобы использовать TypeScript 5.0.
  2. lib.d.ts Изменения: были внесены некоторые изменения в то, как генерируются типы для DOM, что может повлиять на существующий код. В частности, некоторые свойства были преобразованы из числовых в числовые литеральные типы, а свойства и методы для обработки событий вырезания, копирования и вставки были перемещены между интерфейсами.
  3. Критические изменения API: некоторые ненужные интерфейсы были удалены, и были внесены некоторые улучшения корректности. TypeScript 5.0 также переместился в модули.

В TypeScript 5.0 объявлены устаревшими некоторые параметры и соответствующие им значения, включая target: ES3 , out , noImplicitUseStrict , keyofStringsOnly , suppressExcessPropertyErrors , suppressImplicitAnyIndexErrors , noStrictGenericChecks , charset , importsNotUsedAsValues ​​и preserveValueImports , а также добавление в начале ссылок на проекты.

Хотя эти конфигурации останутся в силе до TypeScript 5.5, будет выдано предупреждение, чтобы предупредить пользователей, которые все еще их используют.

TypeScript 5.0 проще, быстрее и компактнее! Узнайте об изменениях, которые произведут революцию в вашей игре по программированию, прямо здесь. Нажмите, чтобы твитнуть

Краткое содержание

В этой статье вы узнали о некоторых основных функциях и улучшениях, которые привносит TypeScript 5.0, таких как улучшения в перечислениях, разрешении сборщика и параметрах типа const, а также об улучшениях скорости и размера.

Если вы думаете о TypeScript для своих следующих проектов, попробуйте хостинг приложений Kinsta бесплатно.

Теперь твоя очередь! Какие функции или улучшения вы считаете наиболее привлекательными в TypeScript 5.0? Есть ли какие-то важные из них, которые мы, возможно, упустили из виду? Дайте нам знать об этом в комментариях.