Pull to refresh

Comments 109

UFO just landed and posted this here

Что-то интересное. Спасибо.

Сорри, плюсик в карму не смогу поставить - уже поставил ранее :)

Можете пояснить в чём именно проблема с отдельным импортом? Только тем что одной строчкой кода больше? Зато в одном месте можно посмотреть все зависимости файла - это же удобно! Плюс если несколько раз используется тип не надо указывать путь повторно.

Все нормальные IDE делают автоимпорт сами - так что печатать даже меньше надо.

Я например пишу на совершенно разных языках (C#, TypeScript, Python, Go) и кто там как импортирует меня меньше всего волнует. Выбираешь язык под задачу и вперёд. По мне так TypeScript на порядок лучше JavaScript - переполз после 15 лет JS на TS и безумно рад. Никогда не пойму зачем страдать с JS если есть TS.

Я бы сказал, что импорт отдельно, использование отдельно десятки лет используется в C/C++/C#, и это нормально.

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

импорт отдельно, использование отдельно десятки лет используется в C/C++

Эхм ... Расскажите про импорт в C/C++ пожалуйста. Я судя по всему вашу мысль не совсем понял. Или вы имеете в виду директиву "#include" прекомпилятора?

При её использовании никаких танцев с бубнами и двойными декларациями не требуется.

Можете пояснить в чём именно проблема с отдельным импортом?

Каждый импорт - это противоположный экспорт. В ES6+ импорт не будет работать без экспорта. Я должен поддерживать пары export - import в соответствии. А это уже плюс две строчки кода. А если я организовываю экспорт из глубины пакета, то и поболее может быть.

В других языках можно без этого. И в JS можно, и в TS тоже можно. Должно хватать просто типа, если мы говорим за типизированные языки. Мне, например, хватает. И экспортами я не заморачиваюсь. А окружающие говорят, что я страдаю и мучаюсь. Вот я и хочу увидеть, что ж я всё-таки пропустил.

UFO just landed and posted this here

В JS нет типов, а в TS все на них построено. Почти всё.

UFO just landed and posted this here

Я вам выше "спасибо" сказал. Буду смотреть, что там по ссылке. Экий вы обидчивый.

UFO just landed and posted this here

Ничего сложного в этом нет.

Пока вы находитесь в пределах одного npm-пакета. Дальше начинаются сложности.

В вашем случае, о котором вы говорите, вы используете public, protected, static

это из какой-то другой статьи, не из этой (сделайте Ctrl +F по "protected")

importJava != include PHP. Первое - это указатель на пакет (адресация), второе - оператор загрузки и выполнения кода (сравните с include_once)

только делаете это нечитаемым

А вот IDE нормально отрабатывает по Ctrl+Click по названию класса. И даже может подсветить его, если в двух разных файлах продекларирован один и тот же класс. Но это для Java и для PHP (современного).

В Magento 2.3 порядка 590 тыс. классов и они свои конструкторы пишут именно так - без include'ов и require. И ведь как-то справляются.

О да, ждём'с нормальный импорт

На вкус и цвет товарища нет.

По мне так, import/export это прекрасно. И не важно как оно реализовано. Вон в Go то что экспортируется просто должно начинаться с большой буквы - без всяких public or export. А в TypeScript нужно написать export да еще пробросить в index.ts. Это всё мелкие детали реализации и делается на автомате.

Нет серебряной пули. За любую возможность приходится платить. И для меня чёткая декларация контракта и централизованное указание зависимостей это благо, а не боль. Так что я с удовольствием пишу export (хотя больше полюбил Go подход). И от import нисколько не коробит. И package.json при всех его проблемах для меня плюс, а не минус.

Вспоминается "вам шашечки или ехать".

За сим откланиваюсь.

import-export в JS - это манипулирование кодом на уровне es-модулей (я сейчас за ES6 говорю). Т.е., файлов. А import в Java - это манипуляция кодом на уровне классов. Т.е. файлов. Как тут уже замечали, в Java один класс - один файл. Но в Java классы-файлы могут находиться не только в файловой системе приложения, но и в JAR-архивах, что порождает JAR hell.

Что мне понравилось в JS, когда я столкнулся с requirejs, так это то, что я могу загрузить в приложение несколько разных версий той же jquery из разных файлов, и все они смогут работать одновременно. Это следствие именно привязки к файлам, а не маппинг имени класса на файловую систему.

В PHP в одном файле может быть много классов, за загрузку исходников отвечает autoloader, а его можно реализовать очень по-разному. Полагаю, что даже можно устроить самому себе что-нибудь типа PHAR hell.

Я привык думать на уровне классов, т.к. в том же PHP в больших проектах, типа Zend'а, придерживаются Java-принципа: один класс - один файл.

UFO just landed and posted this here

А в чём там проблема с выводом типов?

UFO just landed and posted this here

В любом месте использования либо известно какой именно из "одноимённых" типов используется, либо используется их объединение. Так что никакой проблемы не возникает.

UFO just landed and posted this here

Это не тот язык, которым я владею, поэтому в комменте выше меня смущает только ОП. Я решал проблему, каким должен быть JS-код, чтобы он мог загружаться без изменений и в браузере, и в nodejs-приложениях, и пришёл к выводу, что в нём не должно быть межпакетных import'ов. Но это проблема JS, а не проблема Haskell'я. В Haskell'е этой проблемы нет в принципе.

Эта программа должна вывести 10 или 42?

Ошибку компиляции.

UFO just landed and posted this here

Присоединюсь к коллеге @nin-jin - я тоже не вижу проблемы в выводе типов при отсутствии импортов. Должно хватать типов при объявлении переменных и в сигнатурах функций.

Помогает что оптимизировать код, что компилятору находить неиспользуемый

Полагаю, этот "плюс" является следствием наличия экспортов уровня npm-пакета. В Java/PHP весь публичный код пакета (уровня maven/composer) является доступным всем остальным. Где-то это хорошо, где-то может быть плохо.

Необходимость прописывать экспорт в явном виде даёт возможность "скрывать" какую-то часть пакета от внешнего потребителя. Правда всё равно остаётся вариант с прямым доступом к содержимому пакета по имени файла, но это уже "грязный хак" (типа моего DI'я ;)

UFO just landed and posted this here

Лол. А что вы тогда оставляете выводу типов?

Ничего? Я просто не сталкивался с такой проблемой ранее. Это в Хаскеле такие проблемы? Ну, значит это его "обратная сторона медали". Наверное, какой-то профит он всё-таки даёт, раз вы на нём пишите.

UFO just landed and posted this here

Наблюдая за вашими комментариями под статьями, складывается впечатление, что вы не понимаете, зачем вам TS

Вы жили в других парадигмах, пришли в JS со "своим уставом" и удивляетесь, почему это не работает. Вам говорят, как принято в комьюнити сейчас, но вы упорно отказываетесь. На любой аргумент в пользу TS у вас 10 против, это нормально. Не нормально то, что вас должны все убедить перейти на TS под соусом вашей, якобы, открытости к новому.

На все вопросы даже в этой статье есть ответ. Бандлеры (роллап, вебпак, даже тот же гальп как таскраннер), плагины к ним делают те вещи, которые вы хотите, но не так, как вы хотите.

Давайте не будем лукавить: вы не собираетесь переходить на TS, вас не нужно убеждать, потому что кроме вас никто этого сделать не может. Вы не хотите принимать правила игры, потому что они вам не по вкусу, вы заложник своих привычек. С вашей точки зрения импорты не те, то не то, это не так...

К чему эта демагогия?

Чтобы узнать что-то новое.

Не нормально то, что вас должны все убедить перейти на TS

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

Да, я пришёл в JS "со своим уставом". Мне так удобно, я так и программирую. Я просто проверяю "свой устав" "на излом". Пока что никто не смог опровергнуть моё предположение, что без import'а es-модули в JS/TS не связываются. Это то, ради чего статья и была написана. Если же смогут - будет повод покопаться в чём-то новом. Мне не проблема поменять "свой устав", был бы повод.

Пока что никто не смог опровергнуть моё предположение, что без import'а es-модули в JS/TS не связываются.

«Пока что никто не смог опровергнуть мое предположение, что без использования знака «=« переменной нельзя присвоить значение».

Серьезно, это конструкция языка, вот так она задана и с этим ничего не сделать. Конечно, можно найти обходной путь (как в давние времена, когда все файлы просто склеивались в один большой файл, никаких импортов писать не нужно было, и этт было адски неудобно, либо можно написать свой плагин к бейбелю), но лучше смириться с тем как язык спроектирован именно сейчас и как абсолютно всё ожидает что он будет выглядеть (IDÉ, webpack, eslint, Babel, оптимизаторы кода, uglifyjs, да даже браузер с нодой). Синтаксис из php, который вы показали, просто не существует в js/ts

Более того, все надмножества над жс (тот же flow and pure script) используют такой же синтаксис для модулей, да даже сильно другие от жс языка, компилируемые в жс, используют его (elm, dart, …). Причём не потому что не могут ничего другого, а потому что он удобнее и позволяет для каждого места проследить цепочку импортов и понять откуда что берётся.

Серьезно, это конструкция языка, вот так она задана и с этим ничего не сделать. 

Вот рабочий JS-код, в котором есть 4 зависимости и ни одного import'а. В других языках (Java/PHP) это делается давно и привычно. В JS тоже возможно (пример по ссылке).

/** @type {TeqFw_Web_Front_Defaults} */
const DEF = spec['TeqFw_Web_Front_Defaults$'];

Какой ужас.

  • Зачем 2 раза TeqFw_Web_Front_Defaults?

  • Что за магический глобальный массив spec?

  • tree shaking с этим работать будет?

  • Где dependency injection?

this.send = async function (data, factory) {

Зачем внутри конструктора? Сложно же читать что именно конструктор делает.

Зачем 2 раза TeqFw_Web_Front_Defaults?

Первый раз - для IDE, второй раз - для программы. Если сделать плагин для IDE, достаточно будет и одного раза.

Что за магический глобальный массив spec?

Это не глобальный массив, а входной параметр конструктора. Прокси-объект, через который DI-контейнер определяет, какую зависимость запрашивает конструктор. Контейнер по идентификатору находит исходник для зависимости и импортирует модуль (если этот модуль не был импортирован ранее), затем создаёт из (default)экспорта модуля объект и возвращает его в качестве зависимости (или сразу использует ранее созданный, если это singleton). Это если коротко.

tree shaking с этим работать будет?

Никак. Зависимости тянутся сразу на фронт и только те, которые используются в приложении.

Где dependency injection?

Вот.

this.send = async function (data, factory) {

Мне тоже :( Я бы предпочёл приватные атрибуты (#DEF), но Safari их начал понимать только с апреля 2021-го.

Хорошо, давайте по порядку.

  1. В моем полностью типизированном проекте есть куча файлов без импортов. Да, зависимости в них обычно глобальные или нет вообще, но это не показатель сам по себе.

  2. Собственно подразумевается что этот файл кто-то где-то будет импортировать. Хотя признаю, что в ноде легко сделать автоимпорт конкретно для подобного подхода. Но см. пункт 8

  3. Конкретно это пример фабричных методов, которые получают зависимости от аргументов. Для JS нормально в данном случае не иметь импортов, потому что они передаются как аргументы. Осталось только убедиться что они переданы и корректного типа.

  4. Для пункта 3 в TS тоже есть корретный форкфлоу. Более того, он часто даже более предпочтительный. Дело в том, что ts основан на структурном тайпинге, т.е. он проверяет что в переданном объекте есть требуемые поля, но при этом сами объекты могут быть разными. Подробнее тут https://www.typescriptlang.org/docs/handbook/type-compatibility.html Это позволяет переписать ваш пример с ТС так же без импортов.

  5. Большинство типов в вашем примере - "какой-то объект", полезность чего под вопросом, но опустим этот пункт. Местами навешать дженериков всё же не помешает.

  6. Добавьте сюда что-то вроде axios, не загружая им контекст и не вставляя его глобально. Опять же, в ноде не сложно дописать волшебный импорт, который это сделает, а в вебпаке если уж очень хочется есть import() API (раньше был require()), но и то и другое предназначено для других кейсов.

  7. Убедитесь что ваш объект spec передан в корретном виде и поля, которые вы из него читаете, соответствуют типу, который вы ожидаете. Ну или иными словами, получите какую-то пользу от типизации (кроме возможно волшебного DI).

  8. Ну и заставьте хоть какую-то IDE перейти в определение типа по клику. И отрефакторить (переименовать, например).

  9. Возможно ли так писать? Да, конечно, многие из этих пунктов можно обойти, но только никто кроме вас это поддерживать не будет и разрабатывать большой проект на этом будет невозможно. Если бы это было внесено в стандарт -- может вообще бы хорошо было (хотя по пять раз писать один и тот же путь, например, если я работаю с математикой и у меня 10 полей с типом "вектор" слегка бы утомило. И рефакторинг с переносом файла в другое место замусорил бы весь гит правками прямо в коде).

  10. Хотел написать про тестирование, но, справедливости ради, как раз для тестов мокать будет проще.

  11. Да и вообще, если так подумать, это просто другой синтаксис импорта, у которого есть преимущества и недостатки, и без специальных доп программ он тоже не будет работать. Ну вот решили в TS придерживаться синтаксиса ES Modules, и придерживаются. Такие правила игры, никто не мешает форкнуть ТС или написать свой плагин к бейбелю для поддержки именно такого синтаксиса. Хотя есть справедливый вопрос: почему сущности должны импортироваться / экспортироваться одним образом, а типы для них другим?

Ну и бонус: ts без импортов для данного файла, если очень хочется:

interface DEFS {
  SHARED {
    SPACE_API: string
  }
}

interface CONFIG {
  root: string;
  door: string;
  urlBase: string;
  // прочие поля, используемые конкретно этим классом
}

interface SPECS {
   TeqFw_Web_Front_Defaults$: DEFS;
   TeqWf_Web_Front_Api_Dto_Config: CONFIG;
}

export default class TeqFw_Web_Front_Service_Gate {
    constructor(spec: SPECS) {
        const DEF = spec['TeqFw_Web_Front_Defaults$'];
        const config = spec['TeqFw_Web_Front_Api_Dto_Config$'];
        ...
    }
    }
  // думаю, идея понятна: указыаем что используем, тогда ТС проверит что в переданном объекте есть все необходимое

PS. Я так понимаю, синтаксис классов вы тоже не принимаете?

Конкретно это пример фабричных методов, которые получают зависимости от аргументов. Для JS нормально в данном случае не иметь импортов, потому что они передаются как аргументы. Осталось только убедиться что они переданы и корректного типа.

Я когда-то проверял тип входных данных, а потом пришёл к выводу, что это не нужно. В JS, так уж точно. Допустим, что данные пришли не того типа - и что можно сделать? Выбросить исключение, чтобы прервать работу программы? Ну так она и так прервётся, если я начну использовать отсутствующие свойства объекта. А если не начну, то значит объект подходит по своим интерфейсам данному участку кода. Ведь это и есть суть "утиной типизации", разве нет?

При этом я полностью поддерживаю указание типов в коде для входных-выходных аргументов, как помощь разработчику (IDE и транспилятору).

Убедитесь что ваш объект spec передан в корретном виде и поля, которые вы из него читаете, соответствуют типу, который вы ожидаете. Ну или иными словами, получите какую-то пользу от типизации (кроме возможно волшебного DI).

spec - это прокси-объект, который по идентификатору зависимости (вот этому

TeqFw_Web_Front_Api_Dto_Config$) находит соответствующий es-модуль и извлекает из него соответствующий экспорт (default в данном случае) и "готовит" из него объект ( singleton в данном случае), используя экспорт в качестве фабрики (функции или класса), после чего передаёт обратно в конструктор. spec не имеет определённой структуры, а его свойства всегда соответствуют заданному идентификатору, если этот идентификатор в принципе разрешаем в объект/класс/функцию.

Ну и заставьте хоть какую-то IDE перейти в определение типа по клику. И отрефакторить (переименовать, например).

Пожалуйста - это PhpStorm. Наведение курсора мыши с удержанием Ctrl. По клику будет переход на декларацию класса в файле .../@teqfw/web/src/Front/Api/Dto/Config.mjs. Не уверен, что VSCode на такое способен, он с JSDoc-аннотациями хуже работает, чем IDEA.

И рефакторинг с переносом файла в другое место замусорил бы весь гит правками прямо в коде

С import'ами та же проблема, если только вы не маскируете внутрипакетный перенос файла пакетным экспортом. Тогда - да, для внешнего кода экспорт останется тем же. Но внутри npm-пакета перенос файла также отразится на всех импортах использующего кода этого же пакета.

Ну и бонус: ts без импортов для данного файла, если очень хочется:

Спасибо за код. Это несколько не то, что хотелось бы, но всё равно спасибо.

PS. Я так понимаю, синтаксис классов вы тоже не принимаете?

Принимаю. Там прямо в коде и написано export default class ...

spec - это прокси-объект, который по идентификатору зависимости (вот этому

Т.е. это самописный автоимпорт как я и предполагал. Опять же - в ноде сработает без проблем, в вебе уже не все так просто. Вам нужно либо заведомо включить вообще все файлы в бандл, покуда иным способом вы не можете убедиться будет ли он использоваться, либо писать кастомный плагин, который будет понимать этот синтаксис и получите import() (ну или require, просто с необычным синтаксисом). К слову, сам по себе import() поддерживается тс, так что теоретически заменив ваш волшебный spec[…] на import(“root/module/blabla”) может даже сработает ваш импорт.

хочу заметить что в данном случае вы откровенно вводите в заблуждение что у вас нет импортов других модулей. У вас другой синтаксис, но импортом он от этого не перестаёт быть.

рефакторинг обычных импортов лучше тем что он будет править только шапку, файла, но функции как были с коммитами соответствующими правкам, так и останутся. В вашем же примере он их всех подправит с «добавил зависимость для задачи АБ-1234» на «отрефакторил абсолютно не относящийся к этому коду модуль»

Т.е. это самописный автоимпорт как я и предполагал.

Вы правильно предполагали. Отсутствие import в коде не значит, что его нет вообще. Он как раз в загрузчике (autloader'е).

покуда иным способом вы не можете убедиться будет ли он использоваться

Этот "иной способ" - DI-контейнер. Как только к нему приходит запрос на создание объекта и идентификатор этого объекта (вот этот вот TeqFw_Web_Front_Api_Dto_Config$), так он сразу лезет на сервер за исходником для него. PWA предлагают кэшировать исходники на стороне клиента. Я специально разделил код своих модулей на три части (Back, Front и Shared), чтобы можно было делать preload всего кода через service worker'ы. Если этот вариант не устраивает, то есть вариант загрузки on-demand и кэширования только используемого кода. Поначалу приложение будет тормозить, а со второго запуска будет скользить, как по маслу.

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

Если у вас создалось впечатление, что я говорил, что можно создавать многомодульные приложения без импорта исходников, то прошу прощения - я совершенно не имел этого в виду. Я лишь хотел сказать, что импортить модули можно и без того, чтобы указывать в их коде оператор import на каждую зависимость.

Последний абзац не понял. У меня зависимости инжектятся в конструктор, а это аналог импортов в шапке файла. Добавление новой зависимости в конструктор не влияет на тот код, который использует этот конструктор для создания зависимости (конструктор используется только DI-контейнером, в этом его суть - создание иерархии зависимостей). Если же имеются в виду изменения JSDoc-аннотации, то да - везде, где использовался тип Vnd_Prj_Mod1, а стал использоваться Vnd_Prj_Mod2, произойдут изменения. В этом и смысл рефакторинга.

Спасибо за вдумчивые вопросы.

Да, опять же, третий раз, наверное, вы переизобрели https://webpack.js.org/api/module-methods/#import-1

Правда покуда он сугубо асинхронный (в отличие от вашего синхронного, что, к слову, проблематично сделать на жс и будет жутко тормозить первое время пока все не прогрузит), у него основное назначение - разбивать код по отдельным чанкам. Тогда возвращаюсь к моему пункту - хотите пользоваться ts / webpack / миллионы плагинов на все случаи жизни - играйте по их правилам. Хотите свои правила - пожалуйста, но поддерживать это никто не будет. Ну и «их» правила, при желании, можно адаптировать к вашим. Вон, первый ангулар (angularjs который) похожим образом работал, правда ожидал что вы все склеите в один файл, но не сложно доделать до дозагрузки файлов, но что-то не пошло.

P.S. Вообще продуманная система зависимостей позволяет построить дерево зависимостей перед исполнением кода и, таким образом, загрузить все нужные зависимости заранее, а не тормозить UI пока что-то там прогрузится (и, в вашем примере, оно будет генерировать просто тысячи запросов на сервер, что проблематично без http2), а если начнёте делать так (декларировать модули либо заранее, либо декоратором), короче переизобретете inversifyjs или ангулар. Правда оба этих играют по правилам вебпака, но суть это не меняет.

Речь не об этом, а о том, что вы вроде бы обьявили класс, но все методы класса навесили через this в конструкторе. Так никто не делает ни старым синтаксисом, ни новым. Этих методов не будет в прототипе, они будут пересоздаваться каждый раз и вообще выглядит странно, вам вообще класс не нужен в данном случае, просто фабричная функция (ну или как оно там называлось, недоинкапсуляция в жс). В классе есть синтаксис объявления методов, который вы игнорируете без особой на то причине (да, вы замыкаете свой spec, но вообще это можно сделать изящнее и правильнее).

Я был вынужден так делать - Safari не воспринимал #private до апреля этого года, а свой код я писал в прошлом. Я счёл это меньшим злом. Сейчас в коде конструктора можно писать так:

/** @type {TeqFw_Web_Front_Defaults} */
this.#DEF = spec['TeqFw_Web_Front_Defaults$'];

а методы в класс навешивать стандартным способом. Но тут уж не моя вина, я просто добивался совместимости с Safari.

Можно использовать фабричную функцию и даже лучше во многих случаях использовать фабричную функцию (с фронт-шлюзом - так точно), но IDEA лучше понимает код, когда используются классы и методы, а не JSDoc аннотации namespace и memberOf. Я понимаю, что проще использовать TS, чем напрягать IDEA, но иногда проще не значит лучше. Это моя точка зрения, а не универсальная :)

Ну так она и так прервётся, если я начну использовать отсутствующие свойства объекта

Если отствующие свойства, то да. А если свойства есть, но они работаю по другому?
Есть функция, работающая с целыми числами
> function cmp(a,b) {return a > b}
undefined
> cmp(10,9)
true
> cmp(«10»,«9»)
false
или другая, тоже рассчитаная на целые числа, а передаем float. В js вроде все float, но тем не менее пример должен быть понятен.
> function eq(a,b) {return a==b}
undefined
> a=0.3
> b=0.1*3
> eq(a,b)
false

Если использование неверных входных данных не приводит к сваливанию работы программы, то придётся проверять входные данные.

Позволил себе несколько упростить ваш код:

namespace $ {

    export class $TeqFw_Web_Front_Service_Gate extends $mol_object2 {
            
            @ $mol_mem
            base_url() {
                
                const config = this.$.$TeqFw_Web_Front_Api_Dto_Config()
                const defaults = this.$.$TeqFw_Web_Front_Defaults()
                
                const schema = '//';
                const domain = config.urlBase;
                
                let port = location.port; // empty string for default ports (80 & 443)
                if (port !== '') port = `:${port}`
                
                const root = (config.root) ? `/${config.root}` : '';
                const door = (config.door) ? `/${config.door}` : '';
                const space = `/${defaults.SHARED.SPACE_API}`;
                
                return `${schema}${domain}${port}${root}${door}${space}`;
            }

            /** Send API service request to backend. */
            @ $mol_fiber.method
            send< Data extends object >(
                /** JS-object to be sent as request */
                data: Data,
                factory: $TeqFw_Web_Back_Api_Service_IRoute
            ) {
                this.$.$TeqFw_Web_Front_Api_Gate_IAjaxLed.notify()

                const URL = `${this.base_url()}${factory.getRoute()}`;
                const res = this.$.$mol_fetch.json( URL, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({data})
                });
                return factory.createRes(res.data)
            }
            
        }
    }

}

Что меня сразу же смущает в коде, помимо того, что это не JS - что такое @ $mol_mem и @ $mol_fiber.method? Я не смогу понять, во что превращается ваш код после транспиляции, пока не узнаю, что это.

Во-вторых - вы просто выкинули код, который даёт приложению сигнал, что выполняется API-запрос (можно на UI'е выводить индикатор запроса, например) и код, который передаёт в приложение сообщение об ошибке выполнения запроса).

Вы явное сделали неявным, переместив куда-то. Такое себе упрощение.

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

$mol_atom: теория и практика реактивности - тут про мемы.

Quantum Mechanics of Calculations in JS - тут про файберы.

А ещё я убрал глитчи из вашей логики, которые возникают при параллельных запросах.

Песочница тоже не может найти mol_mem и mol_fiber :(

А ещё я убрал глитчи из вашей логики, которые возникают при параллельных запросах.

А вот это уже интереснее. Нагугли, что "глитч" - это "ошибка, которая позволяет получить преимущество, не задуманное разработчиками". И что там за ошибка и кому она даёт преимущество? Я не вижу там места для ошибки даже при параллельных запросах.

Вы статью про MAM-то почитайте.

Глитчи в данном случае - это мерцание интерфейса из-за некорректной множественной смены состояния приложения.

Спасибо. Насколько я понял, "глитчи" - это из области "реактивности". Когда в состоянии приложения возникает инконсистентность. У меня нет своей собственной реактивности и нет своего состояния. Фронт-шлюз отвечает только за отправку запросов на сервер и получение ответов. Кто и как его использует - за кадром.

Я у себя использую Vue 3, если глитчи и возникают, то там. Но добавление Vue 3 к teq-приложению - это тема следующей статьи. Или через одну.

UFO just landed and posted this here

1. import вначале файла говорит о том, что в текущем файле есть зависимости с другими файлами

В любом проекте с числом файлов больше 2-х есть такая зависимость. Я выше демонстрировал, как IDE переходит на декларацию соответствующего элемента кода по ctrl+click (картинка выше по дереву комментов). Зависимость по коду гораздо более информативна, чем зависимость по файлам. Тем более, что import как раз и даёт зависимость по коду:

import {dirname, join} from 'path';

2. указание ссылки (полного пути) на файл внутри конструктора класса/метода/функции - проблема дальнейшего восприятия другими людьми, кто будет поддерживать ваш код

Точно так. Особенно, когда они не сталкивались с таким ранее в том же Java или PHP.

из второго вытекает - если у вас будет зависимостей в конструкторе более 10, вы сами потеряетесь в них же

Во-первых, если у вас более 10 зависимостей (и не важно, через import'ы или конструктор), то это говорит о необходимости декомпозиции кода. Во-вторых, нет, не потеряюсь. Вот пример из Magento 2.4 с конструктором на 14 зависимостей. Это не мой код, а просто пример того, что возможности человека удивительны. В-третьих, в таких случаях всё-таки нужно обратиться к пункту 1.

если ... можно указать 1 импорт,

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

Ваш устав не так важен, так как за Вас уже поработали люди

У меня нет такой веры в людей, как у Вас. Скорее всего, люди поработали так, чтобы хорошо было именно им. Так как и я пишу "свой устав" только для того, чтобы было хорошо именно мне. Чтобы именно я через пару лет смог разобрать тот код, который я написал пару лет назад.

5. Пытаться внести что-то, что удобно вам - не значит, что это будет удобно другим, скорее - это будет им совсем неудобно. Я видел такой код в живую, и это боль - читать такой код.

Извините за причинённые неудобства. Но, как я уже упомянул выше, я не отношусь к альтруистам, а скорее даже наоборот.

Только этот import выглядит иначе

Я и не говорю, что в JS можно подтягивать зависимости без import'а - у меня он тоже есть. Только я использую его более экономно, не в каждом модуле.

я предпочитаю не тратить время на то, что не дает профита

Следуя ваше логике:
- я могу пойти к питонщикам и сказать, что их_такой_синаксисис полное говно
- могу пойти к сишникам/джавистам и сказать, что их синтаксис и типизация слишком сложна, а работу с памятью я не понимаю и нафига оно вообще надо, в JS есть свой сборщик мусора и работает все прекрасно без обращений к нему
- пойти в любой ООП и сказать, что прототипное наследование - отстой, потому что схема не прозрачна, а композиция - лучше! Зачем классы, когда можно процедурно набахать функций, вон с jQuery так все делали!
- пойти в любую команию и сказать, нафига вы делаете микросервисные архитектуры и т.д. и т.п., возьмите ВордПресс, у него пыха не беке и работа с бд уже есть! А еще поставьте плагинчик, он вам и сваггер сам нарисует!
- зачем CI/CD если есть FTP
- зачем GIT если есть архиваторы

и т.д. и т.п.

И когда я скажу, что "я пришел к вам из JS, там все иначе, но я хочу узнать что-то новое", но все мне не будет нравиться, просто потому, что я так не привык...

Короче, глупо все это...

- я могу пойти к питонщикам и сказать, что их_такой_синаксисис полное говно

Можете, и кто-то даже с вами согласится - _поле для защищенных и __поле для приватных, ну что это за дичь!

- могу пойти к сишникам/джавистам и сказать, что их синтаксис и типизация слишком сложна

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

- пойти в любой ООП и сказать, что прототипное наследование - отстой

Прототипное наследование - атрибут JS, в других ОО языках оно не используется.


Но в целом вы конечно правы, это глупо.

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

В пределах одного пакета вполне можно писать вот так:

import { UserService } from '../users';

class UserController {
    constructor(private readonly userService: UserService) {}
}

Достаточно добавить ../users/index.ts с содержанием типа:

export * from './user';
export * from './user.service';
// etc

...и не использовать default export.

Ну и, как выше уже писали, IDE вполне способна сама сгенерировать import для вас. Не нужно делать это руками. Вы ведь в Java вручную import не пишете? Это просто потеря времени.

В пределах одного пакета вполне можно писать вот так:

А в пределах двух?

IDE вполне способна сама сгенерировать import для вас

Т.е., без import'а пока ещё никак. Вы не опровергаете, а только поддерживаете моё мнение.

Современные IDE уменьшают необходимость ручного набора.

Я вставлю тип в код, нажимаю Alt+Enter, у меня появляется список, откуда можно импортировать этот тип. Если есть только один источник - и списка не появляется, сразу автоматически ставится import

Я нажимаю Alt+Shift+O - и неиспользуемые импорты в начале файла исчезают. Так работают современные IDE.

Что касается явного указания export - это происходит по той причине, что нам нужна инкапсуляция, нам нужно открывать для доступа только самое нужное, а кишки прятать.

В Java можно обойтись только public-private, потому что там 1 класс на 1 файл. В JS/TS файле нет таких ограничений. Можно вообще хранить только функции или константы. Поэтому возникает необходимость в export - чтобы отделить внутренние функции модуля от тех, которые мы позволяем использовать.

В JS/TS файле нет таких ограничений. ... Поэтому возникает необходимость

Согласен. Чтобы необходимость не возникала, нужны ограничения.

а в Java так не писали вообще никогда (Java'вский import ничего не подгружает, а работает как alias, можно и без import'ов, если указывать полные имена классов)

По-моему в джаве только так и пишут. Можно указывать полные имена, да, но так мало кто делает, кроме случаев совпадающих имен

Т.е. по факту основная претензия: в JS нельзя использовать полные имена вместо импорта, что почти никому и не нужно

Касаемо необходимости экспорта: вам уже написали, что это просто инкапсуляция. Если не хочется иметь инкапсуляцию и хочется открыть всё, есть вебпак плагин, который сможет все поля/методы/классы экспортировать за вас.

Пс, в джаве с версии 9 тоже есть возможность/необходимость указывать экспорты для модулей. Если бы не backward compatibilty, возможно это сделали бы настройкой по умолчанию.

Соболезную всем кто с вами вынужден работать.

Потому что как говорится — в чужой монастырь со своим уставом не ходят, кажется это на поверхности. И все JS/TS разработчики не смогут вам ничего объяснить, потому что выше в комментах ничего не вышло. У вас есть проблема, которая только ваша проблема. Ее решения нет, потому что ни для кого больше это проблемой не является.

Вот и выходит, что в целом да, можно только соболезновать.

Потому что добавлять импорт для вас боль, а строчить jsdoc на каждую переменную ок.

И всё это ради spec[“import_some_shit_by_forgotten_name”]

По поводу кода вам всё верно написали. Есть большая проблема в типизации аргумента-объекта на TS?

Я бы на код ревью ещё написал — зачем создавать методы внутри конструктора, когда можно определить всё в классе? Дальнейшей типизации это тоже не поможет.

Это я к тому, что данный код выглядит неприятно в целом.

И правильно, что за вас его никто не написал нормально, потому что нужно примерно всё написать аккуратнее и иначе, и это то что надо сделать кроме типизации.

Вы бы ещё удивлялись, почему на PHP нельзя писать как на ассемблере. Тоже можно написать статью, как вы «мучаетесь» с PHP.

Главная проблема автора в нем самом, тезис: "код ненормальный потому что я так не привык" это заведомо отказать себе в развитии.

По поводу ТС: ТС это не язык, это лукавая ООП- бородавка на теле JS. Со всеми признаками сектантства.

И то что вам ТС- ники будут из всех угдов говорить про "лучше" , "проще" и "вапще" и при этом ни строчки кода в пример, это как раз норма, потому, что все, что может ТС это слегка ослабить комплекс неполноценности ООПщика с джавой в анамнезе, попавшего в функциональный мир.

Объективно сложное не бывает простым нигде и никогда, это касается и зависимостей.

JS видит главное проблемой разработки - неоправданную сложность. И ООП и порождение типов на каждом углу - это главный источник роста сложности разрабатываемого решения.

ТС тут не помогает а убивает, подталкивая к тому чтобы плодить типы а не к тому чтобы совершенствовать методы.

Чтобы понять и принять import в js надо не на ТС переходить, а отказаться от примата ООП в своем методическом мышлении и смешать удельный вес решения с объектной модели на совершенствование функций

Плюсуюсь всеми конечности. TS хайп, нужны библиотеки JS, никто не пишет на ваниле и т.д. и т.п. Вот ООП стало хайпом, лепят всюду, когда нужно и не нужно. Отсюда и желание JS сделать настоящим ООП языком, но вот никак не вижу, зачем из него лепить java...

ООП стало хайпом, лепят всюду, когда нужно и не нужно.

Значит, вы все-таки признаете, что ООП нужно. А в том, что люди делают - виновато уже не ООП )

Конечно признаю, и да, ООП классная фича. Вот только использовать бы её по назначению. А так и микроскопом гвозди забивать можно.

ООП стало хайпом

ООП - проверенный десятилетиями подход, с наработанными бестпрактисами. А вот шумиха последних лет вокруг ФП действительно выглядит как хайп.

UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here

Ага, поэтому регулярно появляются статьи, обсуждающие, что же такое на самом деле SOLID/MVC/LSP/etc.

Статьи про то, что же такое в действительности монада появляются примерно с такой же частотой.

При этом почти весь код в ООП-стиле ... в итоге превращался в неподдерживаемое месиво.

Точно так же, как и код в ФП-стиле. Вы не задумывались, может быть дело не в ФП или ООП, а код становится неподдерживаемым по другим причинам?

Статьи про то, что же такое в действительности монада появляются примерно с такой же частотой.

Я вообще сильно удивлюсь, если найдется какая-нибудь сфера деятельности (не обязательно связанная с программированием), относительно которой в интернетах не появляется статей, как "на самом деле" правильно то-то и то-то.

UFO just landed and posted this here

Только в комментах к этим статьям нет таких споров, правильно ли автор понял, что такое монада, или нет.

Потому что монада более простая штука, так как это математическая концепция, и разночтений там поменьше. Но всё-равно могут начаться споры по теоркату.

У меня другой опыт

Ещё бы. Вы больше работаете с ФП-кодом, вам его проще читать. Я больше работаю с ООП-кодом, мне его проще читать. Мне вот, например, сложно разбираться с ФП-кодом на хаскеле, где идут вперемешку композиция кучи функций, оператор применения, flip, и всё сверху полито своими кастомными операторами, у которых ты должен помнить приоритет. Однако, есть и такой код, который легко и понятно читать.

где есть типы и всё такое

Можно подумать, в ООП-языках нет типов. Давайте без снобизма. Конечно, там система типов совсем не выразительная, особенно в какой-нибудь Java, но типы всё-таки есть. Вон есть Typescript с алгебраическими типами. И ничего, пишут на нём нормальный ООП-код (и плохой тоже).

А ещё зачастую в условной java, IDE намного лучше знает о коде и помогает с ним, нежели в haskell, где вроде компилятор типы может вывести, а вот LSP там работает кое-как. Потом смотришь на функцию, и понять не можешь, откуда она взялась. То ли из where-блока, то ли из этого же файла, то ли её TH сгенерил, то ли просто импорт. А редактор подсказать не может, потому что не справляется. Это меня, кстати, больше всего удручает в ФП-языках, компиляторы там умные, но почему-то поддержка IDE никакая. Наверное, только в F# всё более-менее нормально.

UFO just landed and posted this here

При этом почти весь код в ООП-стиле, что я видел, даже написанный людьми, стремившимся следовать этим бестпрактисам, в итоге превращался в неподдерживаемое месиво.

У любого кода есть некий "запас архитектурной прочности". Чем больше забивали на бестпрактисы, тем скорее всё пошло в разнос. К тому же проект сильнее "изнашивается", если его в разное время кодят разные челы, и особенно если отдают на фриланс. Это, в общем, не проблема ООП.

о, мы добавили метод map, у нас теперь функциональщина!

Да кабы на этом остановились, было бы здорово. Мелкие ФП-шные вставки делают код понятнее, тут ничего не имею против.

По поводу ТС: ТС это не язык, это лукавая ООП- бородавка на теле JS. Со всеми признаками сектантства.

Очень жирно. При всех недостатках TS, связанных с его позиционированием, он ближе к функциональным языкам, чем любой из мейнстримовых языков, кроме Scala и может быть Kotlin.

Да в TS не может быть нормального сопоставления с образцом (потому что это рантайм функционал) и нормальной вариантностью пожертвовали (видимо для упрощения написания тайпингов к JS).

Но в TS например нормальный синтаксис для алгебраических типов и нормальный вывод типов при работе с ними. Что позволяет писать хорошо типизированный и поддерживаемый код не используя классов вообще. В типичном React + Typescript проекте классы вообще часто не нужны, а наследование в стиле ES6 вообще почти никогда не нужно.

TS отлично работает в ФП. ООП != явная типизация, вот вообще никак. Haskell чистой слезы ФП, есть типы. Ruby насквозь ООП, типизация неявная

UFO just landed and posted this here

Что вы набросились на дедушку? В СССР тайпскрипта не было!

Его можно понять.

Зато в СССР был Квейсик. И отличался он от Бейсика... типами! И компилятором.

Ничто не ново под луной.

Но вот про импорты не помню.

Дедушка старенький, дедушка в IT уже больше 20 лет. Дедушка уже может выбирать сам, на чём ему программировать и как. Когда был СССР, дедушка программировал на MK-61. Дедушке есть с чем сравнивать.

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

А я в "космос летал" по статьям из "Наука и жизнь" и удивлялся надписи ЕГГОГ на экране, только потом понял, что это ERROR :) А потом считал на нём курсовые на радиофаке, а потом ушёл с радиофака в IT. Да, согласен - это было волшебством.

Я плохо уловил смысловую нить текста, но возможно автору стоит посмотреть https://angular.io/guide/architecture-modules

Я также не понял, чем отличается

    \Magento\Framework\App\Action\Context $context,

от

   require("Magento/Framework/App/Action").Context context,

Вообще при помощи мата и пластыря глобального объекта и Proxy можно попробовать реализовать что-то вроде (насчет синтаксиса не уверен):

Magento.Framework.App.Action.Context context,

При этом стоит помнить, что

  • Импортируемые файлы в ES / TS — это всё-таки модули, и у них есть определенные правила инициализации. Например функция, которую вы импортируете в JS, может хранить в своем замыкании результат вычислений, полученный при первом обращении к модулю.

  • У вас скорее всего отлетит tree-shaking, не говоря уже о module-federation, хотя на бэке это не будет существенно.

Я также не понял, чем отличается

Первое не зависит от структуры файловой системы и вообще чего-либо, в отличии от второго. В Java это не так явно выражено (там есть привязка к classpath) в отличии от PHP, где этот класс может располагаться хоть в архиве, загружаться через eval или вообще храниться в оперативной памяти изначально при загрузке компилятора/интерпретатора.


Помимо этого конструкция "Magento\Framework\App\Action\Context" является декларативной, означающей лишь определение сигнатуры. Если мы говорим про непосредственно пых, то самого класса может вообще не существовать в природе и он даже не будет подгружаться, если метод этот не будет вызываться. В отличии от второго кейса, где инструкция является императивной, требующей загрузки явно по указанному пути.

По поводу отличий коллега @SerafimArts ответил. Первое - декларация типа, второе - я даже не уверен, что это будет работать в PHP.

Вообще при помощи ... глобального объекта и Proxy можно попробовать реализовать

Что-то типа этого и получилось. Контейнер - своего рода "глобальный объект". Узкоспециализированный, только для загрузки модулей, создания объектов и их хранения, но тем не менее практически каждый runtime-объект приложения им порождается.

Импортируемые файлы в ES / TS — это всё-таки модули, и у них есть определенные правила инициализации.

Они продолжают работать. Импорт происходит в неявном виде, просто не применяются импорты в явном.

У вас скорее всего отлетит tree-shaking, не говоря уже о module-federation

А вот тут получается интересно, фронт тянет только те модули, которые он использует. Загрузка модулей на фронт получается on-demand. Поэтому такая схема может использоваться в PWA (с service worker'ами и кэшированием кода), а в обычных web-приложениях будут накладные расходы на загрузку исходников по мере их использования. Но, как я уже объяснял, я во главу угла ставлю свои приоритеты, а лишь затем - интересы бизнеса и пользователя. А для меня важно иметь возможность как можно проще рефакторить свою кодовую базу.

Мне наоборот нравится наличие импортов в мире JS/TS - импортируешь в свой модуль ровно то, что в нем будет использоваться. В итоге у тебя ничего лишнего в подсказке нет - раз. Если будешь смотреть этот файл через энное количество времени - сразу с первых строчек понятно - что в нем будет использоваться - очень удобно.

На яве и пхп писать почти не доводилось, но довольно много пишу под .net и вот там в подсказке ...смешались в кучу люди и кони... и чего в этом хорошего, не знаю, честно. С этим конечно жить вполне можно - иде решает плюс можно удобно все раскидать по неймспейсам, но подход, принятый в js по мне не хуже вот не капельки...

Импорты в ES2015+ существуют в таком виде потому что это необходимо браузерам для статического анализа файла и предзагрузки файлов-зависимостей. Только после этого происходит выстраивание полного AST программы, и начинается компиляция в байт-код. Именно поэтому не подходят варианты in situ, как в PHP или Java, именно поэтому отказались и от нодовского require, который подгружал файл-зависимость прямо на месте. И именно поэтому динамический импорт (который import()) — асинхронный.

Что касается использования файловой системы вместо модульной, то причина всё та же — браузер изначально работает с URL, что позволяет делать вот такие штуки:

import confetti from 'https://cdn.skypack.dev/canvas-confetti';

Как вы понимаете, сделать такое с модульной системой Java или PHP нереально — потребуется пакет-менеджер, в то время как JS успешно работает вообще без него. Можете взглянуть на Deno для примера.

Да, я вот как раз по этим причинам и написал свой DI - пока разбирался, как работает динамический import, и что нужно, чтобы один и тот же код без изменений отрабатывал и в nodejs, и в браузере. Оказалось, что нужно отказаться от межпакетного импорта. Внутри пакета импорты работают одинаково и для nodejs, и для браузера, а межпакетные в браузере не работают.

Сам вопрос интересный, но такой способ его не стала 2Я то э задать -- верный метод отхватить минусов и поднять вашу холивара!

Вообще TS особая штука, там многое изобреталось заново по ходу развития, и отличия от JS именно в ходе мышления программистов -- свой мир, своим терминология и привычки. И, естественно,у них нет ни одной причины доказывать вам что-то или спорить с вами.

Вы ведь не предлагаете им "делайте как я, и у вас всё ускорится, сэкономите кучу времени на экспортах". Вы скорее предлагаете им "признайте, что все это время заблуждались, всё делали коряво и до элементарных вещей не додумались, а я вот сумел". Ну какая тут может быть реакция?)))

Ну, я бы не сказал, что автозагрузка кода - это элементарные вещи :) В Java - возможно. Она с этим родилась, а в PHP были различные варианты, пока не пришли к единому варианту. Я помню времена, когда в каждом более-менее объемном приложении был свой собственный автозагрузчик. Это когда ещё не было composer'а.

А в целом, вы верно отметили и за минуса, и за холивар. Я, как православный буддист, терпимо отношусь к смене веры и языка программирования, но православных буддистов не так уж и много.

MAM - это здорово, а вы не могли бы кинуть ссылку на код своего es-модуля (не ts - я не пойму), который загружается, как в браузере, так и в nodejs-приложении, и у которого в зависимостях есть другие es-модули? Мой - вот. Я понимаю, что мой shared-модуль может загружаться только моим автозагрузчиком. Я просто хочу посмотреть на код вашего.

Это просто структура объекта. Причём именно es-модуль, а не ts, как у вас. Вы можете скопировать этот модуль в вашу девелоперскую среду (например, в mod.mjs) и импортировать его в HTML файл:

<!DOCTYPE html>
<html lang="en">
<head>
    <script>
        import ('./mod.mjs')
            .then((mod) => {
                const Namespace = mod.default;
                const dto = new Namespace();
                debugger
            })
    </script>
</head>
</html>

Разумеется, и html, и mod.mjs нужно выкладывать на web-сервер или отключать CORS в браузере, если запускаться локально с диска.

Я ничего не могу сказать за ваш код, потому что он в таком виде (без транспиляции или транспилятора) не может быть использован в браузере.

Простите, а зачем вообще, чтобы код мог быть использован в браузере as is? Сейчас даже CSS сначала компилируется, оптимизируется, пакуется, зачем же с JS/TS иначе?

Я не пишу таких сложных приложений, где требуется LESS, мне хватает возможностей CSS 5. А с упаковкой и оптимизацией я в Magento напарился - всё свернул и оптимизировал и UI поплыл. А что стало причиной - непонятно.

А вы можете без транспиляции сказать, что значит extends $.$mol_object? Это к вопросу о понимании человеком кода "с листа".

А вы можете без транспиляции сказать, что значит extends $.$mol_object?

Ну здравствуйте, это обычный JS.

Здравствуйте! А где код для этого объекта? Транспилятор его тоже не нашёл. А в своём коде вы тоже не указали, где искать исходник. Что он делает, этот объект? Что мы расширили? Вы пропустили import для исходников этого объекта.

Это обычный неработающий JS.

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

Понимаете, есть языки, а есть диалекты. Есть профессиональные диалекты. Где-то скажут "системный блок электронно-вычислительной машины", где-то "системник", где-то "ящик". Язык один, диалекты разные.

Вы, на самом деле, тоже на своём диалекте говорите, и кому-то он понятен, кому-то нет. Мне вот пример из начала вашей статьи абсолютно не нравится, мне кажется, spec ["very_long_key"] это вообще очень неправильный подход, от которого всеми силами надо бежать.

Но.. ваш диалект, ваше право. Хотите -- на здоровье. Я не хочу -- и пожалуйста)

А то, что для понимания диалекта мало знать сам базовый язык -- это вообще нормально.

Sign up to leave a comment.

Articles