Yamato DaiwaE(CMA)S(cript) extensions

RawObjectDataProcessor — Теоретический минимум

При работе в RawObjectDataProcessor необходимо учитывать некоторые нюансы JavaScript/TypeScript, а вернее, стандарта ECMAScript и связанных с ним. Такие нюансы, разумеется, документированы в официальных и неофициальных источниках, однако начинающие программисты которых немало их часто не знают.

Классификация объектных данных

Как видно из названия «RawObjectDataProcessor», этот утилитарный класс предназначен для работы с объектными данными, то есть данными, удовлетворяющими условию typeof N === "object", при этом не являющимися nulltypeof null тоже является "object" с точки зрения стандарта ECMAScript). Однако, дочерние свойства могут иметь и другие типы: Number, String, Boolean, Тем не менее, на данный момент полностью поддерживаются только входные данные, совместимые с JSON. Соответствующий тип имеет следующее определение:

В будущем возможно будет добавлена поддержка и других типов дочерних свойств, например Date или BigInt. Но на данный момент эта функциональность востребована мало, так как обычно при чтении данных из внешних источников и при образовании их в JavaScript-объект они совместимы с JSON, а если нужны свойства типа Date или BigInt, то их можно преобразовать из строк в ходе постобработки.

Итак, RawObjectDataProcessor работает со следующими четырьмя подтипами объектов:

↓ Тип ключей / ➝ Число элементовФиксированноеПроизвольное
СтроковыеОбъект фиксированной структурыАссоциативный массив
Неотрицательные целочисленныеКортежИндексный массив

Объекты фиксированной структуры

Для этих подтипов объектов подразумевается, что имена всех возможных свойств известны заранее. Тип SampleType из демо является примером такого подтипа объектов:

Фиксированная структура не является запретом на полиморфные свойства, однако возможные варианты должны быть заранее известны, как в примере ниже.

Такой тип объектов является наиболее популярным, потому что чаще всего именно такое данные в сериализованном виде данные отправляются с клиента на сервер и наоборот. Такой же тип данный обычно представляют собой настройки, хранящиеся в файлах наподобие JSON, YAML и так далее (например, конфигурация TypeScript — обычно tsconfig.json).

Объекты типа «ассоциативный массив»

Ассоциативные массивы в отличие от объектов фиксированной структуры могут иметь произвольное количество свойств, имена которых заранее не известны.

До стандарта ESMAScript 2015 в качестве таких массивов использовались обычные объекты, с точки зрения выходного JavaScript-кода ничем не отличающиеся от объектов фиксированной структуры. В TypeScript подобные ассоциативные массивы аннотируются одним из следующих способов:

Введённый в стандарте ESMAScript 2015 тип данных Map можно или не нельзя называть ассоциативным массивом в зависимости от того, допускать ли у такого типа массивов ключи произвольного типа (в частности, Map не запрещает объектные ключи). Так или иначе, тип данных Map несовместим с JSON, однако такой совместимости можно достичь, преобразовав Map в двумерный индексный массив, где в каждом дочернем массиве первый элемент — ключ, а второйзначение:

Другими примерами часто используемых ассоциативных массивов являются многие поля package.jsonscripts, dependencies, devDependencies, peerDependencies и так далее.

RawObjectDataProcessor, разумеется, работает с обычными ассоциативными массивами, которые представляют собой объекты со строковыми ключами, однако не поддерживает входные даынне типа Map ввиду их несовместимости с JSON. Тем не менее, с помощью функциональности постобработки можно преобразовать желаемые данные и к Map.

Индексные массивы

Набор данных, расположенных в памяти непосредственно друг за другом, доступ к которым осуществляется по индексамцелым числам начиная с 0, Как в русском, так и в английском языках так сложилось, что просто «массивами» (array(s)) часто называют именно индексные, а не ассоциативные массивы.

Во многих жёстко типизированных языках программирования (в частности, C++, C# и Java) обычные индексные массивы имеют фиксированное количество элементов и часто единый тип элементов. Если требуется добавление и/или удаление элементов, то используются специализированные коллекции — с точки зрения концепции те же самые индексные массивы, но обеспечивающие возможность добавления и/или удаления элементов за счёт замены одного стандартного массива на другой внутри себя.

В же отношении ECMAScript, индексный массивэкземпляр класснопободного объекта Array (утверждение X instanceof Array истинно). Поскольку такими индексными массивами можно гибко манипулировать (в частности, указывать элементы совершенно разных типов, добавлять элементы и удалять их), то нужды в дополнительных коллекциях пока что нет.

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

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

Кортежи

С точки зрения концепции, кортежимассивы (вообще-то говоря, не только индексные) фиксированного количества элементов.

До стандарта ECMAScript 2025 (не путать с упомянутым выше ECMAScript 2015), в JavaScript не было специализированного инструмента для создания массивов ограниченной длины. В TypeScript термин «кортеж» («tuple») применялся только к индексным массивам, аннотируемым следующим образом:

В стандарте ECMAScript 2025 появился новый тип данных Tuple который помимо того, что должен иметь фиксированное количество элементов, также после инициализации является доступным только доступен для чтения, а потому для гарантии неизменяемости не может содержать в себе объектов никаких подтипов, будь то Array, Map, Set или любой другой объект. Этот тип данных слишком молодой, потому пройдёт некоторое время, прежде чем он станет широко поддерживаться в различных средах выполнения. Также можно предположить, что из-за своих ограничений он не получит широкого распространения, и естественно, RawObjectDataProcessor такой тип данных не поддерживает. Ввиду этого, далее речь будет идти о TypeScript-кортежах, которые уже совместимы с RawObjectDataProcessor.

Кортежи не очень популярны, однако примеры использования имеются. Так, функция useState из React возвращает кортеж из двух элементов. С натяжкой можно назвать «кортежами» конфигурацию многих (но не всех) ESLint-правил, в которых второй элемент опциональный. Тем не менее, на каждой из двух позиций ожидается элемент конкретного типа, что является основным признаком кортежей.