Search
Write a publication
Pull to refresh
4
0
Максим @khmm12

User

Send message

Сравнение с Rust было исключительно в контексте гарантий инвариантов на уровне типов. Если указано, что тип должен иметь определённое поле с конкретным значением или состоять из подобных типов, TypeScript это позволяет выразить и строго гарантирует. На моей памяти не так много не-функциональных языков поддерживают такую выразительность — первым в голову приходит именно Rust.

Но да, вы абсолютно правы: поскольку в TypeScript система типов основана на структурной типизации (structural typing), возможно передать значение, которое является структурным подмножеством ожидаемого. Поэтому два инварианта, одинаковые структурно, с точки зрения TypeScript считаются одним и тем же типом. Аналогично, если, например, ожидается конкретный тип с фиксированными полями, а на вход приходит объект с теми же полями и “довеском” — с точки зрения TypeScript это допустимо.

К счастью, в последние годы всё чаще используются Branded Types, которые позволяют жёстко зафиксировать конкретный тип, делая систему ближе к номинативной типизации (nominal typing).

Примеры
type AccountID = UUID
type ProfileID = UUID

// true
type _ = AccountID extends ProfileID ? true : ProfileID extends AccountID ? true : false
type AccountID = Branded<UUID, 'AccountID'>
type ProfileID = Branded<UUID, 'ProfileID>'>

// false
type _ = AccountID extends ProfileID ? true : ProfileID extends AccountID ? true : false
class Rectangle {}

class Circle {}

function calculateArea(circle: Circle): number {
  return 42
}

// ok, the function accepts
calculateArea(new Circle())

// Oops, the function accepts Rectangle instances as well, because Rectangle structurally equals Circle.
calculateArea(new Rectangle())
class Rectangle {}

const CircleBrand = Symbol('type')

class Circle {
  private [CircleBrand] = 'circle'
}

function calculateArea(circle: Circle): number {
  return 42
}

// ok, the function accepts
calculateArea(new Circle())

// ok, the function doesn't accept non-circle instances
calculateArea(new Rectangle())

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

npm create vite@latest

Готово. Можно начинать решать тривиальную задачу.

Если проблема в том, что нельзя это сделать без сторонних инструментов — тогда по этой логике и C/C++ плохие языки, потому что без QT (или аналогов) сложно нарисовать даже простое окно с двумя кнопками. И таких примеров масса.

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

Есть. Dart (Flutter), Elm, ClojureScript, C# (Blazor).

Ситуация с фронтендом настолько ужасна, что самый популярный и стабильный фреймворк React заставляет вас писать на языке jsx (tsx), который является абстракцией над JS и HTML. Альтернативы еще хуже, например, Svelte также добавляет свой html-js-подобный язык с различными магическими директивами.

В чём же ужас? JSX выглядит чище, чем композиция компонентов в Flutter, Swift UI, Jetpack Compose. Мне правда сложно сказать, что XML-like синтаксис и его использование ужаснее, чем функции (или конструкторы классов), в которые в качестве аргументов передаются результаты функций (или экземпляры классов), приправленное большим количеством обратных круглых скобочек.

В Svelte, кстати, признали ошибку, с 5 версии, ЕМНИП, синтаксис не отличается от ванильного JavaScript.

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

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

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

А в других экосистемах иначе? iOS (Swift), Android (Java/Kotlin), Flutter (Dart). Внутри даже одной компании встречаются проекты, использующие разные архитектурные подходы, файловую структуру, да ещё к тому же разные подходы: Vanilla vs Storyboard vs Swift UI, Vanilla vs Jetpack Compose.

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

С тех пор как stage-0, stage-1 фичи перестали тащить в проекты, прошло много лет. В большинстве современных JS/TS-кодовых баз синтаксис вполне стандартный, без неожиданных сюрпризов.

Далее, TypeScript не дает никаких гарантий. Вообще. Как же так? Компонент в систему добавили, +1 язык выучили, а undefined все еще is not a function? Причина в том, что TS был разработан для gradual adoption, то есть плавной интеграции с JS. Разработчики предположили (и правильно предположили), что просто взять и переписать весь джаваскрипт в мире на TS не получится, и любой TS-код будет взаимодействовать с небезопасным JSом. Как следствие, очень и очень часто в своем TypeScript проекте вы будете видеть тип any который как бы говорит “хз что это, делай с этим что хочешь но на свой страх и риск”.

Предположим. Как это применимо к написанным с 0 проектам на TypeScript? У нас даже джуны знают, что почему нельзя использовать any (или когда можно).

Классная типизация, да?

Да, у TypeScript действительно классная типизация, которая обеспечивает null-safety в отличие от упоминаемого Golang. Не говоря уже о том, что TypeScript легко позволяет перечислить все инварианты и на этапе проверки типов узнать, что где-то контракты не сошлись. Сопоставимые гарантии может дать только Rust, разве что.

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

Как мы видим на практике, как раз решает — и довольно эффективно.

Если вы не писали на Go, советую попробовать, вы удивитесь, насколько жизнь может быть приятнее (как бонус еще и конская производительность)

Отсутствие null-safety, отсутствие потокобезопасности на этапе компиляции, кастрированные дженерики мы опустим. Golang замечательный язык, однако тоже с компромиссами и неверными архитектурными решениями, с которыми приходится мириться.

Вы когда-нибудь думали о том, чтобы создать свой стартап? Почти каждому стартапу (да и +/- любой компании) нужен лендинг/сайт. А у каждого сайта вверху есть хедер с меню навигации.

ИМХО, можно ещё больше сэкономить время и использовать конструкторы сайтов, поддержку которых отдать отделу маркетинга и не тратить драгоценное время, которое можно потратить на основную функциональность стартапа.

При чем в наше время он обязательно должно быть адаптивным, чтобы одинаково хорошо работать как на компьютерах, так и на мобильных телефонах и планшетах.

А ещё поддерживать навигацию с помощью клавиатуры, а для этого, как минимум, необходимо следовать семантике и не использовать div'ы вместо button.

представляющий собой универсальный хедер.

А что делать, если дизайнер хочет переключалку слева?

padding: 0 15px !important;

https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

Тогда другим будет проще стилить.

P.S. Actions Code speaks louder than words (c)
Реальность такова, что те, кто хочет быстро сделать на коленке, скорее всего возьмут готовый CSS фреймворк или фреймворк на основе фреймворка; а остальные потратят меньше времени на разработку ещё одного navbar, чем на monkey-patching готового.

Если это сильное короткое проветривание, то влажность, конечно, не удержать, но и длится оно недолго — увлажнитель не потратит много воды впустую.

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

В моём случае оно появилось скорее как побочный эффект сценария пост-проветривания. Добавить один узел в эту схему – отключи увлажнитель это тривиальная задача.

Поделюсь своим опытом автоматизации такого же увлажнителя. Тоже использую HA + Node-RED.

Для себя определил следующие сценарии:

  1. Выключить экран перед сном. Перевести увлажнитель в ночной режим (низкая скорость). Гигиена сна.

  2. Включить экран утром. Перевести скорость на автоматический режим. Стандартный рабочий режим.

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

  4. После проветривания. Если влажность в помещении более 40% (комфортный минимум), то перевести в автоматический/тихий режим, в противном случае максимальная скорость до достижения минимально требуемой влажности или 15 минут (отсечка по тайм-ауту). Нужно оперативно увлажнить воздух.

  5. Если есть кто-то дома/пришли домой и вода в баке заканчивается, прислать уведомление на телевизор и в телегу.

В остальное время (стандартный режим работы) необходимая влажность задана 60% и увлажнитель сам самостоятельно управляется в автоматическом режиме. По следующим причинам:

  1. Дисковый увлажнитель чисто физически не сможет переувлажнить воздух. Скорость испарения стремительно падает при приближении к точке росы. Поэтому для меня желаемая влажность - максимум.

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

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

useDeepEffect пишется гораздо проще путём глубокой мемоизации его зависимостей.

import { useRef, useEffect, DependencyList } from 'react'

function useDeepCompareDeps(deps: DependencyList | undefined): DependencyList | undefined {
  const ref = useRef(deps)
  if (!isEqual(ref.current, deps)) ref.current = deps
  return ref.current
}

const useDeepEffect: typeof useEffect = (fn, deps) => useEffect(fn, useDeepCompareDeps(deps))
Советую попробовать asdf. Очень похож на rbenv/nodenv, но поддерживает NodeJS, Ruby, Python, Dart, Flutter, Go, Elixir.

Минерализатор служит одной простой цели — изменить вкус воды на выходе. Ни о каком тотальном изменении содержания минералов и речи не идёт, о чём тот же Atoll прямо говорит.

Речь шла только об одном конкретном случае — работа с объектами. Прошу прощения, стоило уточнить.
Spread оператор это синтаксический сахар над Object.assign

const newEntity = { ...original, ...mutation }

Эквивалентно
const newEntity = Object.assign({}, original, mutation)

Information

Rating
11,645-th
Location
Саранск, Мордовия, Россия
Registered
Activity

Specialization

Fullstack Developer
Lead
JavaScript
Golang
TypeScript
Designing application architecture