Как стать автором
Обновить

Комментарии 25

v9s means validations

But... Why??

В случае с v8n, это хоть как-то можно обосновать созвучием - ValidEIGHTioN - лично мне такой способ именования кажется притянутым за уши, но тут уж на вкус и цвет. Но что такое Vi-nine-es и почему это means validation - загадка для меня.

НЛО прилетело и опубликовало эту надпись здесь

О4ь. С5о з0а п7е.

НЛО прилетело и опубликовало эту надпись здесь
Также как l10n и i18n являются сокращенной формой для localization и internationalization.
А так скорее для пафосного названия.

Вопрос не совсем по теме. Почему вы решили перейти на Rollup.js, какие его преимущества перед Webpack'ом?

Rollup на выходе генерирует более компактный код.

Возможно вкусовщина, но мне показалось не удобным читать цепочку снизу вверх - такая последовательность сборки правил чем-то обусловлена (кроме "так получилось")?

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

Обещали "unix way" а оказывается что решение решает только ваши проблемы. Сама идея что результат проверки это true|false|string не правильная- я могу хотеть валидатор который выводит ошибку красиво, или отдаёт её через API который использует коды ошибок, можно придумать кучу вариантов когда ошибка будет чем угодно кроме строки.

При этом решение тривиальное - бомж вариант true|any , тру 1337 вариант который добавит сложностей в типовом использовании - Valid | any. Найти унифицированное решение предлагаю вам (это можно сделать не испортив API)

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

Отобразить красиво или через API - это, пожалуйста, снаружи - ничего не мешает обернуть вызов. Что касается кодирования ошибки или какого-то более сложного типа, то да, в этом смысл, но не все так однозначно. Добавить тип number - быть может, было бы разумно (issues на гитхаб доступны для всех), а вот any - уже не так удобно, ведь это ломает смысл использования TS. Определить тип Valid как некоторый шаблонный тип, который, по дефолту равен true - можно, но тогда нужно еще требовать передавать внутрь либо функцию сравнения, которая будет возвращаеть булевый результат в виде валидно/не валидно - выполнение цепочки ведь должно быть управляемым, и мы хотим остановиться при первом невалидном результате, вернув именно его ошибку. Получается, что смысла делать положительную проверку отличной от true нет смысла, потому что положительная проверка - единственный результат, а вместо строки, да, можно использовать <T>, но тут, опять же, github открыт - будет запрос - будет результат какой-то. А обернуть положительную проверку в объект или что-то иное, опять же, можно и нужно снаружи.

В более унифицированном варианте сигнатура у check должна получиться примерно такой, если я правильно понимаю ваши требования:

type Error<T> = T extends boolean ? never : T;

function check<T = string, E extends Error<T> = Error<T>>(value: any, context: any = {}): boolean | E;

Можно и попроще сделать:

function check<T = string>(value: any, context: any = {}): true | T;

но тогда будет шанс того, что отличить результат с ошибкой (`true`) от корректного значения типа T (тоже true) будет нельзя.

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

Все-таки удалось вам моего червяка разбудить внутреннего. Поэкспериментировал, получилось вот такое решение, как наиболее адекватное:

type Error<T> = T extends boolean ? never : T extends Function ? never : T;
function check<T = string, E extends Error<T> = Error<T>>(value: any, context: any): boolean | E;

Таким образом, если задать в качестве типа T boolean или Function - то мы увидим, что тип значения never. Потому что параметр message у нас опционален, и еще может быть фабрикой. А если мы message не указываем, то возвращает true или false.

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

Я на самом деле не настоящий TS разработчик, поэтому я забыл что TS не умеет работать с типами, а только выводит и проверяет их, так что действительно "validated | any" смысла не имеет, хотя сама идея всё ещё та же.
У Вас кстати та же проблема:

check(value: any, context: any = {}): boolean | string

Результат надо сверять с типом, т.к. "Failed" это вполне себе string который кастится в true

Можно конечно прибегнуть к монаде Maybe но тут опять таки проблема что в TS я не встречал корректной реализации Maybe оформленной пакетом, плюс по сути нужна монада обратная Maybe

Так что я быстро накидал аналог : Playground Link

По правде говоря, я - тоже не настоящий TS разработчик, так, периодически приходится. Что касается каста string в boolean, то он происходит, только если делать нестрогое сравнение (==), а строгое сравнение работает без приведения типа (===). То, что вы накидали по ссылке, я уже встречал, но мне такой подход не сильно нравится, и вот почему: мы создаем экземпляр класса на каждое правило, причем не разово, а при каждом вызове каждого правила. Если вспомнить, что вся цепочка - это проверка лишь одного поля, которая должна повторяться при каждом изменении значения, то штука выглядит довольно накладной. Вместо списка мы получили массив, что, в принципе, почти равноценная замена, даже имеет плюс в том, что можно легко выстроить цепочку привычным образом слева-направо. А вот реализация правил выходит уже не такой удобной - дополнительная фабрика, да и, внешние правила, получается, должны знать о ValidationResult, то есть появляется ненужная, как по мне, связь, ведь сами правила довольно просты. Впрочем, последний пункт можно попытаться обыграть, понятно, что это - набросок.

Думаю, не сильно ошибусь, если скажу, что вы пишите на Haskell или Scala - от решения просто веет ФП. Не подумайте только, что я что-то против имею, просто, как мне кажется, несмотря на наличие всяких функциональных примочек в JS - он плохо переваривает чисто функциональные решения, map, forEach, reduce, и т.д. сильно медленнее for, и, полагаю, что для такой маленькой, но много раз вызываемой части лучше подумать немного об оптимизации. Впрочем, я тут сам хорош - вместо массива использую односвязный список и тоже создаю экземпляры Validator, но только одиножды - при создании цепочки.

Посмотрел на ваше решение, взвесил за и против, и, пожалуй, не вижу большой проблемы в том, чтобы исключить типs boolean и Function из ErrorType. Субъективно, это - меньшее зло, чем генерировать массу экземпляров на каждый вызов.

И, должен сказать спасибо за веское замечание об изначально неверном выборе `boolean | string` - уже переписал с использованием дженериков, завтра на свежую голову подправлю статью.

Я не очень понял почему вашу цепочку можно создать один раз а мой валидатор надо пересоздавать каждый раз, они оба stateless так что проблем быть не должно. И разумеется это быстрее чем создавать их каждый раз. Мне кажется современные JS движки способны это нормально оптимизировать при помощи JIT (да и VM исполнение не должно быть слишком медленным тоже ). Так же можно оптимизировать это просто не вызывая валидатор чаще 10Гц - все равно физически на такое среагировать не успеешь. Да и бесит когда начинаешь например вводить email а форма говорит что он не валидный после первого же символа.

Я на C++ пишу оптимизированную математику, и по опыту - баланс выразительность/оптимизированность надо загибать в выразительность- оптимизировать узкое место проще чем сделать код человекопонятным.

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

static DidPassed<T>(){
	return new ValidationResult<T>(true)
}

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

Да и бесит когда начинаешь например вводить email а форма говорит что он не валидный после первого же символа.

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

Я на C++ пишу оптимизированную математику, и по опыту - баланс выразительность/оптимизированность надо загибать в выразительность- оптимизировать узкое место проще чем сделать код человекопонятным.

Согласен, сам применяю всякие ухищрения только там, где это влияет на скорость работы заметно, понятно, что переписывать обработку массива из 10 элементов через for вместо reduce нет смысла. Быть может, вы и правы - удобнее получать на выходе объект, но как-то пока не могу себя убедить в том, что это даст какие-то ощутимые плюсы. Я исходил из той мысли, что решение должно быть максимально простым.

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

Позволил себе немного переписать ваш вариант таким образом, чтобы он больше соответствовал моему видению. Минус, по-моему, тут только в том, что нужно явно каждый раз передавать значение сообщения, уже не получится просто автоматом как true/false использовать.

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

Строгий тип и простота (конечно это спорные утверждения). Ну и мне кажется в большинстве случаев я бы хотел сообщение об ошибке а не просто true/false , а трюки с === в API я не очень люблю - это не очевидно, люди склонны ошибаться в таких вещах и подобные ошибки не очень хорошо отлавливаются.

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

Я тут тоже пару экспериментов поставил и получил в результате что-то вроде версии 2.0, которая как раз возвращает объект, как у вас, но делает это только в конце, и функцию, которая преобразует в тот вид, что был у меня ранее для тех случаев, когда удобно получать именно значение, и даже порядок правил теперь прямой без массива. Осталось документацию обновить. А что касается строгого равно, так в js это нынче норма, лично я его везде "по умолчанию" использую, как и const вместо let. Без оного и null от undefined не отличить, так что я бы сказал, что в мире js что-то вроде true | ErrorType - тоже норма, но я тут пораскинул мозгами и решил, что кто-то может ожидать именно строго один тип объекта, и преобразовать объект в смешение типов проще, чем наоборот. Так что ещё раз спасибо за разъяснения.

Так сказать, детям на заметку:

Если на создание валидатора у автора ушла неделя, а потом созданным пользовались разработчики в течении 10 месяцев, то отношение затрат на библиотечку к общему ресурсу времени проекта получается катастрофически низким, что-то вроде день-два на человеко-год, в зависимости от количества разработчиков.

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

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

И каждый раз очередной молодой разработчик начинает понимать эти грабли только после нескольких подобных проектов. Это если вообще начинает. Ну и если всё же приблизится к пониманию, то выдаёт вот такие, как в статье, велосипеды, всего за неделю. Единственное, что удивляет - почему ему никто не настучал по рукам с криком "это дорого"? Видимо деменция сделала своё чёрное дело, атрофировала даже то, что уже и так было атрофировано, ну и возражать было "нечем". Как ни удивительно но только в таком случае можно ожидать прогресса. То есть: бардак = двигатель прогресса. А маразм - апостол его.

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

а какую альтернативу вы бы посоветовали?

  1. Велосипеды строить нужно.

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

  3. Но язык должен быть Java, а не Java-Script. Как на сервере, так и на клиенте.

Поскольку бизнес и глубина познания есть принципиально антагонистические явления (бизнес хочет только готовое), найти нишу для расширения глубин познания непросто. Плюс стандартная ситуация дома - жена, дети, кредиты, и все оптом хотят много денег. Отсюда следует, что скорее всего и вам тоже не стоит думать о глубинах и выбирать путь в эту сторону. Но если вы (самоуверенно) относите себя к исчезающе малому меньшинству и считаете, что все условия для вас сложились благополучно, тогда дерзайте и ставьте цель на срок более 5-ти лет. Правда тогда вам стоит забыть о фреймворках и прочей стружке из под примитивного (и тупого) сверла. Вдали вас ждут модели, генераторы, автоматический вывод и прочая computer science. Хотя там вы встретитесь с ещё одной проблемой - как объяснить обезьяне, что нужно сделать, что бы ей стало хорошо. И здесь даже дело не в нахождении способа обучения, а в том, что обезьяна сама не знает, что такое "хорошо".

Поэтому я предпочитаю писать свой мини фреймворк с типизацией и валидацией.

Для опен морс проектов это идеальное решение, а от для хуяк и в продакшн время деньги, от и приходится искать библиотеки среди миллиона их

Есть вот такая вещь, с набором дополнительных разных инструментов - все в README - Zod , проще, чище, JSON поддержка и 9кб размер и тд. Плюс добавлю практическую задачу: валидация полей форм для Vue3 - Vee-Validate+Zod

Даже не знаю - что вам ответить, вроде как вся статья посвящена тому - почему такие решения, как Zod, не всем подходят, но за ссылку спасибо - буду иметь ввиду и эту библиотеку, однако размер пакета там не 9кб, а 470кб. А вот почему не подходят всякие vee-validate, я, вроде бы, достаточно ясно описал. Далеко не всегда удобно привязывать валидацию к представлению, для больших и сложных форм, состоящих из нескольких компонентов лучше делать это на уровне данных.

По всей видимости, мыслю свою мне донести в статье не удалось - надо учиться внятней писать.

9кб в минифицированном виде имелось ввиду.. По поводу vee-validate повторюсь, я привел пример практического использования Zod в тандеме с валидатором форм. Про "удобство" вопрос спорный, но вступать в дискуссия сейчас не буду, ведь весь Ваш опыт с вашим проектом - это лично ваш опыт и ничей другой..

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Изменить настройки темы

Истории