Добрый день, меня зовут Павел Поляков, я Principal Engineer в каршеринг компании SHARE NOW, в Гамбурге в ???????? Германии. А еще я автор телеграм канала Хороший разработчик знает, где рассказываю обо всем, что обычно знает хороший разработчик.

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

Возможности TypeScript, которых нужно избегать

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

TypeScript это сложный язык, который развивался в течение долгого времени. Вначале, команда добавляла свойства, которые были не совместимы с JavaScript. Недавнее развитие все же более консервативно, т.е. более совместимо с возможностями JavaScript.

Как и с любым зрелым языком, нам предстоит принимать сложные решения о том, какие возможности TypeScript применять, а каких избегать. Мы тоже столкнулись с этим вопросом, когда разрабатывали платформу Execute Program (бэк-энд и фронт-энд написан на TypeScript) и когда мы создавали наши TypeScript курсы. Мы сделали выводы. Вот четыре рекомендации о том, какие возможности следует избегать.

Избегайте enum

С помощью enum мы можем дать имена набору констант. В примере ниже HttpMethod.Get это имя для строки GET. Тип HttpMethod полностью идентичен объединению (union) двух литеральных типов, т.е. 'GET' | 'POST'.

enum HttpMethod {
  Get = 'GET',
  Post = 'POST',
}
const method: HttpMethod = HttpMethod.Post;
method; // Evaluates to 'POST'

Есть аргументы за использование enum:

Представьте, что нам внезапно нужно заменить строку POST, в примере выше, на post. Нам нужно лишь заменить значение enum на post и все! Остальной код в приложении только ссылается на элемент enum через HttpMethod.Post, и этот элемент продолжает существовать.

Теперь представьте то же изменение в типе, который представлен объединением, а не enum. Мы определяем объединение как 'GET' | 'POST', а позже решаем изменить его на 'get' | 'post'. Любой код, который пытается использовать 'GET' или 'POST' как HttpMethod теперь возвращает ошибку. Мы должны вручную обновить весь этот код. Определенно это дополнительные действия, если сравнивать с enum.

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

Недостаток enum заключается в том, как они встроены в TypeScript. TypeScript, по идее, это JavaScript, но с дополнительным слоем статических типов. Если мы уберем все типы из TypeScript кода, тогда то, что останется, должно быть рабочим JavaScript. Формальное определение, которое используется в TypeScript документации, звучит как “расширение слоем типов” (type-level extension). Большинство TypeScript возможностей это действительно расширение в слое типов для JavaScript и они не влияют на то как код работает, когда выполняется как JavaScript.

Вот пример расширения типами. У нас есть такой TypeScript код:

function add(x: number, y: number): number {
  return x + y;
}
add(1, 2); // Evaluates to 3

Компилятор проверяет типы данных в коде. Потом он должен сгенерировать JavaScript код. К счастью, этот этап прост: компилятор просто убирает все аннотации типов. В этом случае это значит убрать упоминания :number. То что осталось — это полностью рабочий JavaScript код:

function add(x, y) {
  return x + y;
}
add(1, 2); // Evaluates to 3

Большинство свойств TypeScript работают так же. Они соответствуют правилу “расширение на уровне типов”. Чтобы получить JavaScript код компилятор просто убирает аннотации типов.

К сожалению ,enum нарушают это правило. HttpMethod и HttpMethod.Post были частью типа, так что они будут удалены, когда TypeScript сгенерирует JavaScript код. Но, если компилятор просто уберет enum типы из нашего примера, то у нас все равно останется JavaScript код, который ссылается на HttpMethod.Post. Это вызовет ошибку во время исполнения. Мы не можем ссылаться на HttpMethod.Post, если компилятор его удалил!

/* This is compiled JavaScript code referencing a TypeScript enum. But if the
 * TypeScript compiler simply removes the enum, then there's nothing to
 * reference!
 *
 * This code fails at runtime:
 *   Uncaught ReferenceError: HttpMethod is not defined */
const method = HttpMethod.Post;

TypeScript решает эту проблему нарушая свое же правило. Когда компилируется enum, компилятор вызывает дополнительный JavaScript код, который никогда не присутствовал в оригинальном TypeScript коде. Только несколько возможностей, которые предоставляет TypeScript, работают таким образом. Реализация каждой из них добавляет сложностей при компиляции, хотя в целом модель TypeScript достаточно проста. Именно по этой причине мы рекомендуем не использовать enum, а вместо этого использовать объединения.

Почему вообще важно правило расширения на уровне типов?

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

Как может Babel, webpack и множество их плагинов и все другие инструменты в экосистеме полностью поддерживать TypeScript? Для большей части TypeScript правило расширения на уровне типов делает их работу относительно простой. Эти инструменты просто удаляют аннотации типов и оставляют работающий JavaScript.

А когда речь идет о enum (а еще namespace, о них тоже скоро поговорим), то ситуация усложняется. Теперь недостаточно просто удалить enum. Инструменты должны превратить enum HttpMethod {...} в рабочий JavaScript код, несмотря на то, что в JavaScript нет enum.

Это приводит нас к конкретной проблеме с TypeScript, который нарушает свое же правило расширения на уровне типов. Для инструментов типа Babel и webpack работа с TypeScript это всего лишь опция, одна из многочисленных возможностей. В результате, иногда поддержка TypeScript не получает столько внимания, как поддержка JavaScript, а это ведет к багам.

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

Когда ваш компилятор, бандлер, минификатор, линтер, форматировщик кода и т.п. без явных внешних признаков просто неправильно компилируют часть вашей системы, то вам будет сложно с этим разобраться. Баги компилятора очень сложно отдебажить. Прочтите внимательно: “в течение недели, с помощью моих коллег, нам удалось лучше понять где именно происходит баг”. Хотите так же? Нет.

Избегайте namespace

Определения namespace — они как модули, кроме того, что несколько namespace могут быть расположены в одном файле. Например, мы можем создать файл, который определяет отдельный namespace для кода, который экспортируется и код для тестов. (Мы не рекомендуем так делать, но так проще показать что делают namespace)

namespace Util {
  export function wordCount(s: string) {
    return s.split(/\b\w+\b/g).length - 1;
  }
}

namespace Tests {
  export function testWordCount() {
    if (Util.wordCount('hello there') !== 2) {
      throw new Error("Expected word count for 'hello there' to be 2");
    }
  }
}

Tests.testWordCount();

На практике namespace вызывают проблемы. В секции про enum выше, мы говорили о правиле расширения на уровне типов в TypeScript. Обычно, компилятор удаляет все аннотации типов и остается рабочий JavaScript.

namespace нарушают это правило так же как и enum. В namespace Util { export function wordCount ...} мы не можем убрать определения типов. Потому что весь namespace является определением типа в TypeScript! Что случится с остальным кодом вне namespace, когда будет вызван Util.wordCount(...)? Если мы удалим namespace Util до того, как сгенерируем JavaScript код, то Util больше не будет существовать. Так что функция Util.wordCount(...) даже в теории не может работать.

Как и с enum, компилятор TypeScript не может просто удалить определения namespace. Вместо этого он должен сгенерировать JavaScript код, которого не существует в оригинальном TypeScript коде.

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

Избегайте декораторов (пока что)

Декораторы это функции, которые изменяют или заменяют другие функции (или классы). Вот пример декоратора, который мы взяли из официальной документации:

// This is the decorator.
@sealed
class BugReport {
  type = "report";
  title: string;

  constructor(t: string) {
    this.title = t;
  }
}

Декоратор @sealed намекает на sealed модификатор в C#, который запрещает другим классам наследование “запечатанного” класса. Мы можем реализовать его с помощью sealed функции, которая берет класс и изменяет его, чтобы предотвратить возможное наследование.

Декораторы были сперва добавлены в TypeScript, до того, как начали процесс своей стандартизации в JavaScript (ECMAScript). К январю 2022 декораторы все еще находятся в стадии 2 предложения ECMAScript. Стадия 2 — это “черновик”. Предложение о стандартизации декораторов как будто застряло в чистилище ECMAScript комитета — оно находится в стадии 2 с февраля 2019.

Мы рекомендуем избегать декораторов, пока предложение хотя бы не доберется до стадии 3 (кандидат). Или стадии 4 (завершено), для более консервативных команд.

В принципе, есть шанс, что ECMAScript декораторы никогда не будут приняты комитетом. В таком случае, их реализация в TypeScript окажется рядом с enum и namespace. Они продолжают нарушать правило TypeScript про расширение на уровне типов. Также существует риск, что что-то пойдет не так, когда вы будете использовать инструменты, отличающиеся от официального компилятора TypeScript. Мы не знаем произойдет это или нет, но преимущества декораторов минимальны, лучше просто подождать.

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

Исключите использование слова private

В TypeScript есть два варианта как сделать свойства класса приватными. Есть старое ключевое слово private, которое свойственно только TypeScript. И есть новый синтаксис #somePrivateField, который заимствован из JavaScript. Вот пример, где показываются два варианта:

class MyClass {
  private field1: string;
  #field2: string;
  ...
}

Мы рекомендуем использовать новый #somePrivateField синтаксис по простой причине: оба варианта одинаковы. Мы хотим поддерживать паритет характеристик с JavaScript, если у нас нет серьезных причин его нарушать.

Резюмируем

Теперь резюмируем наши рекомендации:

  1. Избегайте enum

  2. Избегайте namespace

  3. Предпочитайте #somePrivateField а не private somePrivateField

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

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

А еще...

Здесь говорю опять я, Павел. В конце еще раз приглашу вас в свой Telegram-канал. На канале Хороший разработчик знает я минимум три раза в неделю простым языком рассказываю про свой опыт, хард скиллы и софт скиллы. Я 15+ лет в IT, мне есть чем поделиться. Все это нужно разработчику, чтобы делать свою работу хорошо, работать в удовольствие, быть востребованным на рынке и получать высокую компенсацию.

А для любителей картинок и историй есть ???? Instagram.

Комментарии (27)


  1. AndreyTS
    02.02.2022 11:38
    +27

    Иными словами, предлагается не использовать возможности языка TypeScript, т.к. автор изначальной статьи не очень верит в first-class поддержку инструментами этого языка.

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

    Как мы не отказываемся от циклов for/foreach в серверных языках по причине лишь того, что в ассемблер или в IL они не транслируются "как есть", а выражаются через goto, так же и нет смысла отказываться от идиоматичных средств языка из-за особенностей трансляции в нижележащий "технический" язык исполнения.


    1. Alexufo
      02.02.2022 11:48
      -10

      Не только автор
      twitter.com/youyuxi/status/1488039446167490561

      и не оглядываться на JS,

      А почему не оглядываться на JS? Бандлы по 2 мегабайта, это нормально? Это же ужасно фигачить на js как на java а потом удивляться размеру бандла.

      Как мы не отказываемся от циклов for/foreach в серверных языках по причине лишь того

      потому что у циклов for/foreach «удобство vs перформанс» имеет значительный перевес в сторону удобства и совсем незначительную просадку в производительности, если она вообще есть.
      А тут вы предлагаете закрыть глаза на то, что js в принципе не типизируется. И хрен бы с ним с объемом выходного кода.

      Думаете, микрософт так сильно думает за производительность в целом? Они сделали блейзор, шарп на васме, так там такой мат и ругань от тех кто его использует, что кажется отдел разработки блейзора решил насильно портить жизнь.


      1. AndreyTS
        02.02.2022 12:20
        +12

        Мне кажется, на размер бандла прежде всего влияют зависимости в большом количестве. А не enum.

        Сколько будет в среднем проекте этих enum? 10? 20?

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

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


  1. ganqqwerty
    02.02.2022 11:39
    +2

    С декораторами я бы ещё привёл в пример Angular как фреймворк, активнейшими образом их использующий.


    1. lexey111
      02.02.2022 12:23
      +1

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

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

      Та же фигня с CLI-тулзами (не только ангуляр, в том же реакте то же самое) - есть шаблон типового поведения - нет понимания, не говорю уже о контроле, что там происходит. Не то, что я хочу поныть в стиле "раньше на курсах вождения карбюратор на время собирали, а сейчас видосик 5 минут смотрят где какая педаль, и всё". Это, скорее, профессиональный перекос, потому что меня постоянно зовут то ng-eject делать, то просто руками новый бандлер написать, когда задача перерастает возможности стандартного тулчейна.

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

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

      Сорри за офтопик, просто как раз сижу разгребаю один проект-переросток на ангуляре.


      1. radtie
        02.02.2022 13:06
        +2

        Думаете много людей понимают как работают / устроены хуки?

        Магия это неотъемлемая часть высокоуровневых фреймворков.


        1. lexey111
          02.02.2022 13:19
          +1

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

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

          "Я пять проектов написал на ангуляре, зачем мне ваш жаваскрипт знать?"

          (я, если что, не спрашивал сложение массивов с объектами - банальные this, замыкания, шедулер в общих чертах, передача по ссылке... народ претендовал на сеньоров/стронг мидлов).

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


      1. SanoLitch
        02.02.2022 13:22

        Сомневаюсь, что много реакт-разработчиков хорошо понимаю как работают те же хуки, проброс контекста и прочий подобный функционал прикрытый сахаром и магией. Тоже касается vue и composition api.


    1. Melonorm
      02.02.2022 14:05
      +2

      В NestJs они тоже очень активно используются.


  1. heilage
    02.02.2022 13:46
    +10

    Ах этот замечательный испанский стыд, когда автор оригинальной статьи рекламирует свои курсы по typescript, но при этом const enum осилить не смог:)


    1. nin-jin
      02.02.2022 16:30
      +3

      const enum не может быть странспилирован пофайлово. А непофайловая транспиляция сильно бьёт по скорости ребилда.


  1. symbix
    02.02.2022 14:16
    +9

    Прямо подборка вредных советов. Какое кому дело, во что там компилируется typescript?

    Особенно с js-овским #private, об использовании которого надо миллион раз подумать, поскольку сериализовать такой объект с сохранением его внутреннего состояния просто невозможно.

    А декораторы с reflect-metadata уже много лет как стандарт индустрии.


    1. JustDont
      02.02.2022 15:07
      +2

      Особенно с js-овским #private, об использовании которого надо миллион раз подумать, поскольку сериализовать такой объект с сохранением его внутреннего состояния просто невозможно.

      Это вполне нормально, что вопрос о возможности сериализации того или иного объекта должен решаться с участием этого объекта. А не то, что внешние силы чего-то там сериализуют как им удобно.


      1. symbix
        02.02.2022 20:38
        +2

        Смотря с какой целью. Если задача в том, чтобы сохранить состояние программы между (пере)запусками или выгружать-загружать из оперативки на диск по мере нужды (будь то персистенция Aggregate Root с помощью DataMapper в парадигме DDD, или что-то наподобие PhantomOS), объекту как раз ни к чему об этом знать: это ведь просто инфраструктурная деталь (ограниченность объема и неустойчивость к сбоям питания оперативной памяти), а не бизнес-логика.

        Проблема JS-овских private как раз в том, что они лишают возможности решить эту задачу даже на уровне фреймворка (как, скажем, позволяет сделать, пусть с определенными неудобствами и оговорками, Reflection в Java).


        1. JustDont
          02.02.2022 20:58

          объекту как раз ни к чему об этом знать

          В этом случае объекту всего лишь достаточно удовлетворять определенному интерфейсу. Т.е. в яве быть Serializable, а в терминах нашего разговора — не иметь приватных полей, или не хранить там ничего относящегося к состоянию.
          И это опять же выбор объекта, быть ли ему таким или эдаким, вы не можете просто так проигнорировать все слои абстракции под объектом, заявив, что вы хотите сделать что-то такое, что "всего лишь инфраструктурная деталь". Объекту лучше знать, можно ли над ним совершить такое действие, или таки нет. Игнорируя абстракции и их неизбежные протечки, вы так завтра начнете писать /dev/urandom в БД до исчерпания потока, да еще и будете возмущаться, что у вас с этой деятельностью что-то пошло не так.


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

          Рефлексия никогда не является вычислительно бесплатной, и если она мне не нужна — я предпочту её не иметь вообще, чем иметь всегда, и не пользоваться (ява, да).


          PS: Да и вообще говоря, дело не в приватности — ваш пример про сериализацию уже требует от объекта намного более детального и конкретного контракта, чем просто "не пользуйтесь #" (тут и по чтению полей будет много соображений, и по записи — еще больше). Так что вы сильно лукавите, когда ставите этот случай в упрек яваскриптовым приватным полям.


          1. symbix
            02.02.2022 21:43

            Разработчику универсального фреймворка или библиотеки типа Hibernate как раз таки неважно, что там за объекты будут, ему важно предоставить универсальное решение для персистенции.


            1. JustDont
              02.02.2022 21:48

              Универсальное решение для персистенции вообще всего, очевидно, невозможно. За исключением случаев, когда необходимые для этого механизмы не будут обеспечены внешним миром (т.е. средой выполнения, например). Во всех остальных случаях — вам придётся таки договариваться о том, что у вас там будут за объекты. Даже если таковые договоренности достаточно простые.


              PS: даже и в яве с её безусловной рефлексией нельзя персистировать что угодно.


              1. symbix
                03.02.2022 13:41

                Паттерн Data Mapper напрямую подразумевает разделение самого объекта и описания механизма персистенции. В этом суть паттерна. Никакой магии, конечно, не произойдет (кроме элементарных случаев), маппинг придется описывать. Но объекту про само наличие этого маппинга знать не надо ровным счётом ничего.


                Дизайн языка, который не позволяет применить один из самых распространенных (а в рамках парадигмы DDD по сути обязательных) паттернов проектирования — прямо скажем, спорное решение.


                1. JustDont
                  03.02.2022 14:51

                  Я совсем не понимаю, с чем вы всё еще пытаетесь спорить.


                  Но объекту про само наличие этого маппинга знать не надо ровным счётом ничего.

                  Объекту всегда (включая эти ваши хибернейты) нужно удовлетворять критериям пригодности к использованию в маппере.
                  "Знать" не нужно, нет. Иметь совместимый с маппером интерфейс — да.


                  1. symbix
                    03.02.2022 17:04

                    И какой же "интерфейс" нужно иметь POJO для "совместимости"?


                    1. JustDont
                      03.02.2022 17:36

                      А сфигали в хибернейте POJO? Откуда тогда взялось дополнительное требование к no-arguments constructor? Или тут POJO, тут не POJO, тут рыбу заворачивали?


                      Все эти дополнительные требования — это и есть описание интерфейса.


                      1. symbix
                        03.02.2022 19:10

                        В Hibernate (в отличие от JPA) нет требования no-arguments constructor. Да, без дополнительных плясок, связанных со спецификой Class.newInstance() (что, на самом деле, ужасно неудобное ограничение Java, и часто приходится извращаться через платформоспецифичные методы типа Unsafe.allocateInstance), не обойтись — но прежде всего для того, чтобы не было требования no-arguments constructor, в Hibernate и существуют интерцепторы.


  1. JustDont
    02.02.2022 15:05
    +1

    На самом деле это советы разного уровня полезности.
    Самый полезный — enum. Потому что рефакторинг енумов, действительно, очень легко может превратиться в рантайм-ошибки — а вообще-то главным образом TS используется именно для того, чтоб как можно меньше проблем с типизацией протекало в рантайм.
    Только это происходит не тем путем, что описан в статье, конечно (там транспиляция не пройдет, всё ок), а при изменении, например, правой части строковых енумов: было someEnum.A = 'A', отрефакторили по любой причине в someEnum.A = 'a' — и удачи найти все места в коде, которые теперь могут отработать неправильно.
    На этом фоне изменения в union type, которые точно не пройдут транспиляцию — это просто рай, пусть даже и придется сделать больше правок в коде при рефакторинге. А учитывая, что в текущем TS union type спокойно создаются из константных списков — разница с енумом осталась довольно маленькой, и строго не в пользу енума.


    Совет про namespace скорее вредный, аргумент "ой это нельзя просто взять и удалить из TS-кода, получив корректный JS-код" не выдерживает никакой критики. Ну нельзя, и что?


    Совет про декораторы — "средненький". Используя декораторы, очень желательно понимать, как конкретно они работают в целом, и как конкретно — вот этот, который вы используете. Тогда хотя бы если что-то начнет ломаться по причине очередного сдвига стандартов и их черновиков — вы будете иметь представление о том, что же именно могло сломаться. Декораторы в прошлом пару раз уже (потенциально) ломались.


    Совет про # — приемлемый. Если у вас тулинг это всё поддерживает, то действительно, пишите # вместо private, почему нет. Но это не повод прям агрессивно избегать private, рефакторить имеющийся код, и совершать подобные резкие движения.


  1. k12th
    02.02.2022 16:56

    Ну делать enum типа `Get = 'GET'` действительно довольно бесссысленно и этого стоит избегать:) Алгебраический тип тут справится гораздо лучше.

    А вот вещи типа `green = '#00ff00'` ложатся на перечисления куда лучше. Это ведь типизированный маппинг, так сказать.


  1. ganqqwerty
    03.02.2022 01:31

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


  1. Myxach
    03.02.2022 03:00

    Я ещё больше задумываюсь о том, насколько страшно веб программирование, что базовые вещи любой другой сферы под запретом


  1. RiverFlow
    03.02.2022 13:43

    Тайпскрипт вообще не нужно использовать!

    И переходить уже от ООП болота е нормальному программированию.