Типобезопасность в JavaScript: Flow и TypeScript
Все, кто имеют дело с разработкой UI в кровавом enterprise наверняка слышали о «типизированном JavaScript», подразумевая под этим «TypeScript от Microsoft». Но кроме этого решения существует как минимум ещё одна распространённая система типизации JS, и тоже от крупного игрока IT-мира. Это flow от Facebook. Из-за личной нелюбви к Microsoft раньше всегда использовал именно flow. Объективно это объяснял хорошей интеграцией с существующими утилитами и простотой перехода.
К сожалению, надо признать, что в 2021 году flow уже значительно проигрывает TypeScript как в популярности, так и в поддержке со стороны самых разных утилит (и библиотек), и пора бы его закопать поставить на полку, и перестать жевать кактус перейти на де-факто стандарт TypeScript. Но под этим хочется на последок сравнить эти технологии, сказать пару (или не пару) прощальных слов flow от Facebook.
Зачем нужна безопасность типов в JavaScript?
JavaScript это замечательный язык. Нет, не так. Экосистема, построенная вокруг языка JavaScript — замечательная. На 2021 год она реально восхищает тем, что вы можете использовать самые современные возможности языка, а потом изменением одной настройки системы сборки транспилировать исполняемый файл для того, чтобы поддержать его выполнение в старых версиях браузеров, в том числе в IE8, не к ночи он будет помянут. Вы можете «писать на HTML» (имеется ввиду JSX), а потом с помощью утилиты babel (или tsc ) заменить все теги на корректные JavaScript-конструкции вроде вызова библиотеки React (или любой другой, но об этом в другом посте).
Чем хорош JavaScript как скриптовый язык, исполняемый в вашем браузере?
Но и, разумеется, это плохо. Потому что сам факт наличия чего-нибудь неработающего где-нибудь на сайте это плохо. И было бы здорово до того, как код попадёт на работающий сайт, проверить все-все скрипты на сайте и убедиться, что они хотя бы компилируются. А в идеале — и работают. Для этого используются самые разные наборы утилит (мой любимый набор — npm + webpack + babel/tsc + karma + jsdom + mocha + chai).
Если мы живём в идеальном мире, то все-все скрипты на вашем сайте, даже однострочные, покрыты тестами. Но, к сожалению, мир не идеален, и для всей той части кода, что не покрыта тестами, мы можем только понадеяться на какие-нибудь автоматизированные средства проверки. Которые могут проверить:
Данный код является корректным с точки зрения синтаксиса языка. Но с точки зрения семантики он некорректен — попытка вызова метода у null вызовет сообщение об ошибке во время выполнения программы.
Кроме ошибок семантики могут быть ещё более страшные ошибки: логические. Когда программа отрабатывает без ошибок, но результат совсем не тот, что ожидался. Классический со сложением строк и чисел:
Существующие средства статического анализа кода (eslint, например), может попытаться отследить значительное количество потенциальных ошибок, которые программист допускает в своём коде. Например:
Добавление типов переменных и проверка вызовов с учётом типов — это дополнительные ограничения языка JavaScript, чтобы уменьшить количество потенциальных ошибок.
Попытка умножить число на строку
Попытка обратиться к несуществующему (неописанному в типе) свойству объекта
Попытка вызвать функцию с несовпадающим типом аргумента
Если мы напишем этот код без средств проверки типов, то код упешно траспилируется. Никакие средства статического анализа кода, если они не используют (явно или неявно) информацию о типах объктов, не смогут эти ошибки найти.
То есть добавление типизации в JavaScript добавляет дополнительные ограничения на код, который пишет программист, но зато позволяет найти такие ошибки, которые в противном случае случились бы во время выполнения скрипта (то есть скорее всего у пользователя в браузере).
Возможности типизации JavaScript
| Flow | TypeScript |
|---|---|
| Возможность задать тип переменной, аргумента или тип возвращаемого значения функции | |
| Возможность описать свой тип объекта (интерфейс) | |
| Ограничение допустимых значений для типа | |
| Отдельный type-level extension для перечислений | |
| «Сложение» типов | |
| Дополнительные «типы» для сложных случаев |
Оба «движка» для поддержки типов в JavaScript обладают примерно одинаковыми возможностями. Однако если вы пришли из языков со сторогой типизацией, даже в типизированном JavaScript есть очень важное отличие от той же Java: все типы по сути описывают интерфейсы, то есть список свойств (и их типы и/или аргументы). И если два интерфейса описывают одинаковые (или совместимые) свойства, то их можно использовать вместо друг-друга. То есть следующий код корректен в типизированным JavaScript, но явно некорректен в Java, или, скажем, C++:
Подобное поведение значительно уменьшает количество проблем при работе с разными библиотеками, пользовательским кодом и даже просто с вызовом функций. Типичный пример, когда некоторая библиотека определяет допустимые опции, а мы передаём их в виде объекта options:
Обратите внимание, что тип OptionsType не экспортирован из библиотеки (и не импортирован в пользовательский код). Но это не мешает вызывать функцию с использованием литерального интерфейса для второго аргумента options функции, а для системы типизации — проверить этот аргумент на совместимость типов. Попытка сделать что-то подобное в Java вызовет у компилятора явное непонимание.
Как это работает с точки зрения браузера?
Ни TypeScript от Microsoft, ни flow от Facebook не поддерживаются браузерами. Как впрочем и самые новые расширения языка JavaScript пока не нашли поддержки в некоторых браузерах. Так как же этот код, во-первых, проверяется на корректность, а во-вторых, как он исполняется браузером?
Ответ — траспилирование. Весь «нестандартный» JavaScript код проходит через набор утилит, которые превращают «нестандартный» (неизвестный браузерам) код в набор инструкций, который браузеры понимают. Причём для типизации всё «превращение» заключается в том, что все уточнения типов, все описания интерфейсов, все ограничения из кода просто удаляются. Например, код из примера выше превращается в…
Такое преобразование обычно делается одним из следующих способов.
| Flow | TypeScript |
|---|---|
| webpack.config.js | |
| Настройки транспилера | |
| babel.config.js | tsconfig.json |
| .flowconfig | |
Разница между подходами babel+strip и tsc с точки зрения сборки небольшая. В первом случае используется babel, во-втором будет tsc.
Но есть разница, если используется такая утилита как eslint. У TypeScript для линтинга с помощью eslint есть свой набор плагинов, которые позволяют найти ещё больше ошибок. Но они требуют, чтобы в момент анализа линтером у него была информация о типах переменных. Для этого в качестве парсера кода необходимо использовать только tsc, а не babel. Но если для линтера используется tsc, то использовать для сборки babel будет уже неправильно (зоопарк используемых утилит должен быть минимальным!).
Типы для библиотек
Когда библиотека публикуется в npm-репозитории, публикуется именно JavaScript-версия. Предполагается, что опубликованный код не нужно подвергать дополнительным преобразованиям, чтобы использовать его в проекте. То есть код уже прошёл необходимую траспиляцию через babel или tsc. Но тогда информация о типах в коде уже, получается, потеряна. Что делать?
Типы для legacy-библиотек
И для flow, и TypeScript есть способ добавления описаний типов для тех библиотек, которые эти описания не содержат изначально. Но сделано оно по разному.
Для flow «нативного» способа, поддерживаемого самой компанией Facebook не предложено. Но есть проект flow-typed, который собирает в своём репозитории такие определения. Фактически параллельный для npm способ версионирования таких определений, а также не очень удобный «централизованный» способ обновления.
У TypeScript стандартным способом написания таких определений является их публикация в специальных npm-пакетах с префиском «@types». Для того, чтобы добавить себе в проект описание типов для какой-либо библиотеки достаточно подключить соответствующую @types-библиотеку, например @types/react для React или @types/chai для chai.
Сравнение flow и TypeScript
Попытка сравнить flow и TypeScript. Отдельные факты собраны из статьи Nathan Sebhastian «TypeScript VS Flow», часть собрана самостоятельна.
Нативная поддержка в различных фреймофорках. Нативная — без дополнительным подходом с паяльником и сторонними библиотеками и плагинами.
Различные линейки
| Flow | TypeScript | |
|---|---|---|
| Основной contributor | Microsoft | |
| Сайт | flow.org | www.typescriptlang.org |
| GitHub | github.com/facebook/flow | github.com/microsoft/TypeScript |
| GitHub Starts | 21.3k | 70.1k |
| GitHub Forks | 1.8k | 9.2k |
| GitHub Issues: open/closed | 2.4k / 4.1k | 4.9k / 25.0k |
| StackOverflow Active | 2289 | 146 221 |
| StackOverflow Frequent | 123 | 11 451 |
Смотря на эти цифры у меня просто не остаётся морального права рекомендовать flow к использованию. Но почему же я использовал его сам? Потому что раньше была такая штука, как flow-runtime.
flow-runtime
flow-runtime это набор плагинов для babel, который позволяет встроить типы от flow в runtime, использовать их для определения типов переменных в runtime, и, самое главное для меня, позволяло проверять типы переменных в runtime. Что позволяло в runtime во время, например, автотестов или ручного тестирования отловить дополнительные баги в приложении.
То есть прямо во время выполнения (в debug-сборке, разумеется), приложение явно проверяло все-все типы переменных, аргументов, результаты вызова сторонних функций, и всё-всё-всё, на соответствие тех типов.
К сожалению, под новый 2021 год автор репозитория добавил информацию о том, что он перестаёт заниматься развитием данного проекта и в целом переходит на TypeScript. Фактически последняя причина оставаться на flow для меня стала deprecated. Ну что же, добро пожаловать в TypeScript.
Сделайте свой код JavaScript надежным с Flow
Russian (Pусский) translation by Ilya Nikov (you can also view the original English article)
JavaScript всегда был важным языком программирования, являясь единственным языком, который надежно работает в браузере. Недавние тенденции в области разработки на начальном этапе, а также основанная на Node.js разработка основаны на масштабах и сложности приложений JavaScript.
Большие приложения, разработанные крупными командами, могут воспользоваться проверкой статического типа, которой не хватает в ванильном JavaScript. Flow был разработан Facebook для решения этой проблемы. Это статический тип проверки, который интегрируется в ваш процесс разработки, улавливает множество проблем на раннем этапе и помогает быстро двигаться.
Что такое Flow?
Flow против TypeScript
Прежде чем погрузиться в подробные детали Flow, стоит сравнить его с другими альтернативами, и в частности с TypeScript. TypeScript является строгим расширением JavaScript, разработанным Microsoft. Любая программа JavaScript также является программой TypeScript.
У TypeScript отличный инструмент и поддержка IDE. Flow идет вверх (например, JetBrains WebStorm имеет встроенную интеграцию Flow).
Важнейшее философское различие заключается в том, что Flow делает упор на надежность. В TypeScript 1.0 не были обнаружены ошибки; TypeScript 2.0 со строгими проверками нуля, измеренными до Flow в этом отношении. Но в других аспектах, таких как общие контейнеры или типизация, TypeScript является более разрешительным и пропускает различные категории ошибок (проверяется только структурная типизация, а не номинальная типизация).
TypeScript как собственный язык добавляет концепции и языковые функции, такие как классы, интерфейсы, индикаторы видимости (общедоступные, частные, только для чтения) и декораторы. Эти функции упрощают понимание и использование для людей, пришедших с основных объектно-ориентированных языков, таких как C ++, Java и C #.
Установка
Вы можете добавить несколько скриптов в файл package.json для автоматизации процесса:
Перед публикацией кода в реестр npm вы должны запустить сценарий prepublish.
Для других параметров установки (например, с использованием npm или babel) ознакомьтесь с руководством по установке Flow.
Чтобы завершить установку, введите: yarn run flow init
Система типов
Flow имеет две важные цели: точность и скорость. Его система типов была разработана для поддержки этих целей.
Точность
Точность достигается путем анализа того, как код взаимодействует с типами, как аннотированными, так и предполагаемыми. Любое несоответствие вызывает ошибку типа. Аннотированные типы поддерживают номинальную типизацию, что означает, что два разных типа с одинаковыми атрибутами отличаются друг от друга и не могут быть заменены. Тип переменной определяется как набор значений времени выполнения, которые может получить переменная.
Скорость
Flow быстрый благодаря сочетанию модульности и распределенной обработки. Файлы анализируются параллельно, и результаты объединяются позже через эффективную общую память для выполнения полнопрофильной проверки типов.
Поддерживаемые типы
Flow поддерживает множество типов. Помимо примитивных типов, он также поддерживает следующее:
Типы аннотаций
Flow позволяет объявлять типы, а также ограничивать переменные и параметры выбранными значениями:
Если вы превысите допустимый диапазон, вы получите сообщение об ошибке:
Чтобы исправить это, давайте вернем молодого воина, если оба воина имеют одинаковую силу:
Flow обеспечивает еще более точный контроль через расширение класса, инвариантность, коразмерность и противоречие. Ознакомьтесь с документацией по Flow.
Конфигурация
Include
Ignore
В любом нетривиальном приложении JavaScript используется множество сторонних библиотек. Flow может проверить, как ваше приложение использует эти библиотеки, если вы предоставляете специальные файлы libdef, содержащие информацию о типе этих библиотек.
Flow автоматически сканирует подкаталог вашего проекта для файлов libdef в потоковом типе, но вы также можете указать путь к файлам libdef в разделе [libs]. Это полезно, если вы поддерживаете центральный репозиторий файлов libdef, используемых несколькими проектами.
Импорт существующих определений типов и создание собственного, если целевая библиотека не предоставляет собственные определения типов, довольно проста:
Lints
Flow имеет несколько правил lint, которые вы можете контролировать и определять, как их обрабатывать. Вы можете настроить правила из командной строки, в комментариях кода или в разделе [lints] вашего файла конфигурации. В следующем разделе я расскажу о linting, но вот как его настроить, используя раздел [lints] :
Опции
В разделе [options] вы можете сообщить Flow, как вести себя в самых разных случаях, которые не заслуживают отдельного раздела, поэтому все они сгруппированы вместе.
Есть слишком много вариантов, чтобы перечислить их все здесь. Некоторые из наиболее интересных:
Version
Flow и его формат файла конфигурации развиваются. Раздел [version] позволяет указать, какая версия Flow сконфигурирована для предотвращения ошибок.
Если версия Flow не соответствует настроенной версии, Flow отобразит сообщение об ошибке.
Вот несколько способов указать поддерживаемые версии:
suggest : предложить типы для целевого файла
Linting с Flow
Правила
В настоящее время существует три правила: all, untyped-type-import, и sketchy-null. Правило «all» действительно является обработкой по умолчанию для любых ошибок, которые не имеют более конкретного правила. Правило «untyped-type-import» вызывается при импорте типа из нетипизированного файла. Правило «sketchy-null» вызывается, когда вы выполняете проверку существования значения, которое может быть ложным или null/undefined. Существуют более подробные правила для:
Уровни серьезности
Линтинг с аргументами командной строки
Линтинг с комментариями flowlint
Существует три типа комментариев: flowlint, flowlint-line и flowlint-next-line.
Комментарий «flowlint» применяет набор правил в блоке до тех пор, пока не будет отменен соответствующий комментарий:
Если нет соответствующего комментария, настройки просто применяются до конца файла.
«flowlint-line» применяется только к текущей строке:
«flowlint-next-line» применяется к строке, следующей за комментарием:
Заключение
Большие проекты JavaScript, разработанные крупными командами, могут значительно выиграть от статической проверки типов. Существует несколько решений для введения проверки статического типа в кодовую базу JavaScript.
JavaScript продолжает расти в Интернете. Это не лишено кривых обучения, и есть много фреймворков и библиотек для вашего изучения. Если вы ищете дополнительные ресурсы для изучения или использования в своей работе, посмотрите, что у нас есть на рынке Envato.
СОДЕРЖАНИЕ
Вступление
Программирование на основе потоков определяет приложения, используя метафору «фабрики данных». Он рассматривает приложение не как единый последовательный процесс, который начинается в определенный момент времени, а затем выполняет одно действие за раз, пока не завершится, а как сеть асинхронных процессов, обменивающихся данными посредством потоков структурированных блоков данных, так называемые «информационные пакеты» (IP). В этом представлении основное внимание уделяется данным приложения и преобразованиям, применяемым к ним для получения желаемых результатов. Сеть определяется внешне по отношению к процессам как список соединений, который интерпретируется программным обеспечением, обычно называемым «планировщиком».
Поскольку процессы FBP могут продолжать выполняться до тех пор, пока у них есть данные для работы и место для вывода результатов, приложения FBP обычно работают за меньшее время, чем обычные программы, и оптимально используют все процессоры на машине, без специального программирования. для достижения этой цели.
Определение сети обычно схематично и преобразуется в список соединений на каком-либо языке нижнего уровня или в нотации. FBP часто является визуальным языком программирования на этом уровне. Более сложные определения сетей имеют иерархическую структуру, состоящую из подсетей с «липкими» соединениями. Многие другие потоковые языки / среды выполнения построены на более традиционных языках программирования, наиболее ярким примером является RaftLib, который использует операторы, подобные iostream C ++, для определения потокового графа.
FBP имеет много общего с языком Linda в том смысле, что он, по терминологии Гелернтера и Карриеро, является «координационным языком»: он по существу не зависит от языка. Действительно, при наличии планировщика, написанного на достаточно низкоуровневом языке, компоненты, написанные на разных языках, могут быть связаны вместе в единую сеть. Таким образом, FBP поддается концепции предметно-ориентированных языков или «мини-языков».
FBP продвигает высокоуровневый функциональный стиль спецификаций, который упрощает рассуждения о поведении системы. Примером этого является модель распределенного потока данных для конструктивного определения и анализа семантики распределенных многосторонних протоколов.
История
Обычно в IBM эти концепции назывались «потоком данных», но этот термин считался слишком общим, и в конечном итоге было принято название «программирование на основе потоков».
С начала 80-х до 1993 года Дж. Пол Моррисон и архитектор IBM Уэйн Стивенс усовершенствовали и продвигали концепции, лежащие в основе FBP. Стивенс написал несколько статей, описывающих и поддерживающих концепцию FBP, и включил материал об этом в несколько своих книг. В 1994 году Моррисон опубликовал книгу, описывающую FBP и предоставляющую эмпирические доказательства того, что FBP приводит к сокращению времени разработки.
Концепции
На следующей диаграмме показаны основные элементы диаграммы FBP (кроме информационных пакетов). Такая диаграмма может быть преобразована непосредственно в список соединений, который затем может быть выполнен соответствующим механизмом (программным или аппаратным).
На самом деле это образ обработки данных с конвейера : IP-адреса, перемещающиеся по сети процессов, можно рассматривать как виджеты, перемещающиеся от станции к станции на конвейере. «Машины» можно легко переподключить, вывести в ремонт, заменить и так далее. Как ни странно, это изображение очень похоже на изображение оборудования для записи единиц, которое использовалось для обработки данных до дней компьютеров, за исключением того, что колоды карт приходилось переносить вручную с одной машины на другую.
Примеры
«Проблема с Telegram»
Вот наиболее естественное решение в FBP (в FBP нет единого «правильного» решения, но оно кажется естественным):
где DC и RC обозначают «DeCompose» и «ReCompose» соответственно.
Пакетное обновление
Этот тип программы включает передачу файла «деталей» (изменений, добавлений и удалений) в «главный файл» и создание (по крайней мере) обновленного главного файла и одного или нескольких отчетов. Программы обновления, как правило, довольно сложно закодировать с использованием синхронного процедурного кода, так как два (иногда больше) входных потока должны синхронизироваться, даже если могут быть мастера без соответствующих деталей, или наоборот.
В FBP компонент многократного использования (Collate), основанный на идее единичной записи Collator, значительно упрощает написание этого типа приложения, поскольку Collate объединяет два потока и вставляет IP-адреса в скобках для обозначения уровней группировки, что значительно упрощает логику нисходящего потока. Предположим, что один поток (в данном случае «мастера») состоит из IP-адресов со значениями ключей 1, 2 и 3, а IP-адреса второго потока («подробности») имеют значения ключей 11, 12, 21, 31, 32, 33. и 41, где первая цифра соответствует значениям главного ключа. Используя символы скобок для представления IP-адресов «в скобках», выходной поток с сортировкой будет выглядеть следующим образом:
Поскольку не было мастера со значением 4, последняя группа состоит из одной детали (плюс скобки).
Структура вышеуказанного потока может быть кратко описана с использованием BNF- подобных обозначений, таких как
Процессы мультиплексирования
Потоковое программирование очень естественным образом поддерживает мультиплексирование процессов. Поскольку компоненты доступны только для чтения, любое количество экземпляров данного компонента («процессов») может выполняться асинхронно друг с другом.
Когда в компьютерах обычно был один процессор, это было полезно при большом количестве операций ввода-вывода; теперь, когда машины обычно имеют несколько процессоров, это становится полезным, когда процессы также интенсивно загружают процессор. На схеме в этом разделе показан один процесс «Load Balancer», распределяющий данные между тремя процессами, обозначенными S1, S2 и S3, соответственно, которые являются экземплярами одного компонента, которые, в свою очередь, передаются в один процесс в порядке очереди. первому обслуживающему «основанию».
Простая интерактивная сеть
Поскольку разные запросы могут использовать разные серверные части и могут требовать разного количества времени для серверных компонентов (если они используются) для их обработки, необходимо обеспечить связь возвращаемых данных с соответствующими запрашивающими транзакциями, например, хеш-таблицами или кешами.
Сравнение с другими парадигмами и методологиями
Структурированное программирование Джексона (JSP) и разработка систем Джексона (JSD)
Эта методология предполагает, что программа должна быть структурирована как единая процедурная иерархия подпрограмм. Его отправной точкой является описание приложения как набора «основных строк» на основе структур входных и выходных данных. Затем одна из этих «основных линий» выбирается для управления всей программой, а остальные требуется «инвертировать», чтобы превратить их в подпрограммы (отсюда и название «инверсия Джексона»). Иногда это приводит к так называемому «конфликту», когда требуется разбить программу на несколько программ или сопрограмм. При использовании FBP этот процесс инверсии не требуется, поскольку каждый компонент FBP можно рассматривать как отдельную «главную линию».
FBP и JSP разделяют концепцию обработки программы (или некоторых компонентов) как парсера входного потока.
В JSD дизайн сохраняется как дизайн сети до финальной стадии реализации. Затем модель преобразуется в набор последовательных процессов по количеству доступных процессоров. Джексон обсуждает возможность прямого выполнения сетевой модели, существовавшей до этого шага, в разделе 1.3 своей книги (курсив добавлен):
Спецификация, созданная в конце этапа System Timing, в принципе, может быть выполнена напрямую. Необходимая среда будет содержать процессор для каждого процесса, устройство, эквивалентное неограниченному буферу для каждого потока данных, и некоторые устройства ввода и вывода, через которые система подключена к реальному миру. Такая среда, конечно, может быть обеспечена подходящим программным обеспечением, работающим на достаточно мощной машине. Иногда такое прямое выполнение спецификации возможно и даже может быть разумным выбором.
М.А. Джексон признал FBP подходом, который следует его методу «декомпозиции программы на последовательные процессы, взаимодействующие с помощью механизма, подобного сопрограммам».
Аппликативное программирование
WB Ackerman определяет аппликативный язык как язык, который выполняет всю свою обработку с помощью операторов, применяемых к значениям. Самым ранним из известных прикладных языков был LISP.
Компонент FBP можно рассматривать как функцию, преобразующую свой входной поток (потоки) в свой выходной поток (потоки). Затем эти функции объединяются для выполнения более сложных преобразований, как показано здесь:
Если мы помечаем потоки, как показано, строчными буквами, то приведенную выше диаграмму можно кратко представить следующим образом:
Так же, как в функциональной нотации F может использоваться дважды, потому что он работает только со значениями и, следовательно, не имеет побочных эффектов, в FBP два экземпляра данного компонента могут работать одновременно друг с другом, и поэтому компоненты FBP не должны иметь побочных эффектов. или. Функциональная нотация может явно использоваться для представления хотя бы части сети FBP.
Тогда возникает вопрос, могут ли сами компоненты FBP быть выражены с использованием функциональной нотации. WH Burge показал, как выражения потока могут быть разработаны с использованием рекурсивного, прикладного стиля программирования, но эта работа была в терминах (потоков) атомарных значений. В FBP необходимо иметь возможность описывать и обрабатывать блоки структурированных данных (IP-адреса FBP).
Линда
Объектно-ориентированное программирование
В статье К. Эллиса и С. Гиббса проводится различие между активными и пассивными объектами. Пассивные объекты включают в себя информацию и поведение, как указано выше, но они не могут определить синхронизацию этого поведения. С другой стороны, активные объекты могут это сделать. В своей статье Эллис и Гиббс утверждают, что активные объекты имеют гораздо больший потенциал для развития обслуживаемых систем, чем пассивные объекты. Приложение FBP можно рассматривать как комбинацию этих двух типов объектов, где процессы FBP будут соответствовать активным объектам, а IP-адреса будут соответствовать пассивным объектам.

