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

Что такое машинописный текст-rtti?

Тот факт, что JavaScript — динамический язык и не имеет собственной системы проверки типов, является головной болью для большинства разработчиков. В этом случае нам на помощь приходит TypeScript, добавляющий дополнительный этап компиляции, на котором мы можем выполнять проверку типов. Однако из-за его природы как языка, который компилируется в JavaScript, который является слабо типизированным/нетипизированным языком, у вас нет RTTI (R). un Time Type Information), как и в таких языках, как C++ или Java, где вы можете проверять типы объектов во время выполнения, используя такие механизмы, как отражение.

Чтобы лучше продемонстрировать, чего не хватает TS, приведу пример. Допустим, у нас есть такой класс Movie:

class Movie {
  name: string
  rating: number
  releasedAt: Date
}

Как мы можем записать, какие свойства имеет этот класс? Просто: мы не можем. Потому что он будет скомпилирован в класс JS следующим образом:

class Movie {
}

Это потому, что свойства в JavaScript существуют только после того, как они имеют какое-то значение. Если для каждого свойства не присвоено значение по умолчанию, они просто игнорируются компилятором.

Давайте предположим, что мы взяли на себя задачу предоставить значение по умолчанию для каждого свойства каждый раз, когда мы создаем класс. Что, если мы хотим проверить, является ли свойство необязательным? Это статическое свойство или свойство экземпляра? А как насчет его неизменности? А что насчет его типа? Это объединение нескольких типов или определенного нами класса? Это тип перечисления? Примитив или объект? У нас просто нет возможности узнать ответы на эти вопросы с помощью ванильного TypeScript.

К счастью, существует пакет typescript-rtti, который позволяет нам получать доступ к информации о типе во время выполнения, и он имеет множество приложений, о некоторых из которых я расскажу позже.

Монтаж

Сначала нам нужно настроить ts-patch — пакет, который позволяет другим разработчикам писать сценарии, которые будут запускаться во время компиляции и вносить изменения в скомпилированный код с помощью API компилятора TypeScript. Не беспокойтесь, что это сломает ваш код, поскольку оно изменит код только во время компиляции и фактически не изменит исходный код. Также не беспокойтесь о том, что вы не знаете, как работает API, потому что вам это не понадобится, если вы не хотите углубиться во внутреннюю работу пакета. Чтобы установить ts-patch, просто запустите следующую команду npm:

npm i -D ts-patch

После установки ts-patch нам нужно настроить typescript на использование компилятора ts-patch вместо своего собственного. Запустите следующее:

npx ts-patch install

Чтобы избежать необходимости запускать этот сценарий каждый раз после npm install, добавьте следующий сценарий подготовки в файл package.json:

{
 /* ... */
 "scripts": {
   "prepare": "ts-patch install -s"
 }
}

Теперь, когда мы закончили установку ts-patch, давайте установим typescript-rtti. Выполните следующую команду:

npm install typescript-rtti reflect-metadata

Этот пакет использует reflect-metadata для хранения информации о типе в виде метаданных на этапе компиляции и использования ее во время выполнения, чтобы мы могли получить доступ к информации о типе. Поэтому вам нужно будет импортировать его как можно раньше в приложении и убедиться, что он импортирован только один раз. Я предлагаю добавить оператор импорта в первую строку вашего основного файла (файла, который запускается первым).

import "reflect-metadata";

Нам также необходимо зарегистрировать typescript-rtti в ts-patch в качестве плагина, добавив следующую строку в раздел compilerOptions внутри вашего файла tsconfig.json:

// tsconfig.json
"compilerOptions": {
    "plugins": [{ "transform": "typescript-rtti/dist/transformer" }]
}

И все готово! Вы можете проверить, правильно ли работает пакет, следующим образом:

// main.ts
import 'reflect-metadata'
import { reflect } from 'typescript-rtti'

class Movie {
  name: string
  rating: number
  releasedAt: Date
}

const propertyNames = reflect(Movie).propertyNames
console.log(propertyNames) // [ 'name', 'rating', 'releasedAt' ]

Как видите, мы наконец-то можем проверять свойства, определенные в классе во время выполнения! Потенциальные возможности применения функции propertyNames уже весьма обширны. А теперь представьте, чего мы можем достичь, используя возможности других функций этого пакета!

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

Стеккадемический

Спасибо, что дочитали до конца. Прежде чем уйти:

  • Пожалуйста, рассмотрите возможность аплодировать и следовать автору! 👏
  • Следуйте за нами в Twitter(X), LinkedIn и YouTube.
  • Посетите Stackademic.com, чтобы узнать больше о том, как мы демократизируем бесплатное образование в области программирования во всем мире.