Open Znack opened 4 years ago
Остановились на этом:
Окей, а как предлагаешь на уровне типов ограничивать конструирование данных, требующих специальных правил, например, как UUID или Email? При этом чтобы только через смарт конструкторы можно было такого рода данные конструировать, где зашиты все нужные проверки. Я бы хотел сигнатуру создания юзера иметь получая типы Email, Phone и UUID, а не string, string, string на входе :)
@sk1e ждём твоего выхода :)
Остановились на этом:
Окей, а как предлагаешь на уровне типов ограничивать конструирование данных, требующих специальных правил, например, как UUID или Email? При этом чтобы только через смарт конструкторы можно было такого рода данные конструировать, где зашиты все нужные проверки. Я бы хотел сигнатуру создания юзера иметь получая типы Email, Phone и UUID, а не string, string, string на входе :)
@sk1e ждём твоего выхода :)
есть какой-то код? Без примеров непонятно
Чтобы номинативную типизацию прикрутить к какому-нибудь типу и ограничить возможность конструирования значения — форсить использовать смарт конструкторы. Например, для UUID. Чтобы юзер не мог UUID собрать никак кроме как через спец конструкторы, а у них уже можно на типах отразить, что не всякая строка — валидный UUID.
function uuidFromString(x: string): Option<UUID> {}
function uuidFromNumbers(a: number, b: number, c: number, d: number): UUID {}
Обрати внимание, что из не факт, что UUID полчится, а из 4 чисел — точно можно однозначно сделать, поэтому во второй функции нет Option.
Теперь когда мы на уровне типов гарантировали, что все данные типа UUID являются валидными, мы можем это использовать в остальных функциях нашей предметки:
createUser(phone: Phone, email: Email, uuid: UUID): User {}
А иначе мы бы имели тип
createUser(phone: string, email: string, uuid: string): User {}
Это и повышает удобство использования типов, это помогает мне легче ориентировать внутри функции createUser
, я например, точно могу знать не только что телефон валиден, так ещё и уверен, в каком формате этот телефон (со знаком + в начале или без и тд)
Зачем нам на фронте собирать uuid?
На фронте телефон и имейл с 99% будет вводиться пользователем, валидироваться внешним валидатором. Как туда интегрировать смарт-конструкторы?
Такие даннные из формы просто кладутся в запрос на сервер. Больше мы с этими сущностями не работаем. Попытка получать гарантии формата на уровне типа выглядит переусложнением для таких случаев. Согласен? Есть какие-нибудь другие кейсы?
Это частая практика. как минимум идемпотентности добиться. Удобно для управления сложными формами создания, как на Птичке, мы там активно UUID на клиентской стороне пользуемся
На фронте телефон и имейл с 99% будет вводиться пользователем, валидироваться внешним валидатором
Что значит внешним валидатором?
Почему 99%? И разве это отменяет факта валидации на клиенте?
Такие даннные из формы просто кладутся в запрос на сервер. Больше мы с этими сущностями не работаем.
Если у тебя тонкий клиент, то ок. Я не про такие случаи. Как быть в случае сложных и толстых клиентов?
На фронте телефон и имейл с 99% будет вводиться пользователем, валидироваться внешним валидатором
Что значит внешним валидатором?
валидатором с определённой сигнатурой, которая нужна библиотеке которая работает с формами
Почему 99%?
Мне сложно представить клиент который будет много работать с телефоном и имейлом
И разве это отменяет факта валидации на клиенте?
Нет, проблема валидации тут в невозможности интегрировать смарт-конструторы в библиотеки по работе с формами. Если они не свои конечно.
Такие даннные из формы просто кладутся в запрос на сервер. Больше мы с этими сущностями не работаем.
Если у тебя тонкий клиент, то ок. Я не про такие случаи. Как быть в случае сложных и толстых клиентов?
Ок, допускаю кейсы, где это даст ощутимую пользу, но что если в какой-то момент нам потребуется рекорд? И Map нам не подойдёт. Будешь повторно вызывать смарт-конструктор при каждом извлечении элемента из рекорда?
В этот очень редкий случай можно как минимум prop
использовать для получения свойства. Здесь проблемы от костыльного использования рекордов однозначно не перевешивают преимуществ от возможности более выразительных типов
В этот очень редкий случай можно как минимум
prop
использовать для получения свойства. Здесь проблемы от костыльного использования рекордов однозначно не перевешивают преимуществ от возможности более выразительных типовочень редкий
Мне кажется ты необъективен) Если у тебя толстый клиент который обильно использует эту модель, то вероятность наличия состояния отражения из этой модели во что-нибудь - наоборот, довольно высокая.
prop
что это такое?
Не понял совсем вот этого, можешь подробнее?
Если у тебя толстый клиент который обильно использует эту модель, то вероятность наличия состояния отражения из этой модели во что-нибудь - наоборот, довольно высокая.
И тем более не понял почему вот это :)
Мне кажется ты необъективен)
prop что это такое?
Это из примера от @in19farkt про то, что доступ к ключу рекорда нормально в TS работает, если не напрямую через obj[key]
обращаться, а написать свою функцию prop(obj, key)
, которая то же самое делает
Не понял совсем вот этого, можешь подробнее?
ID, номер телефона, почта, - всё это свойства моделей которые могут выступать первичными критериями поиска. Если в приложении есть некоторая динамическая коллекция, которая может всячески редактироваться, сортироваться, то оформлять состояние элементов этой коллекции, как правило, лучше всего через рекорд, где ключами будут такие первичные критерии поиска. Клиент который значительно работает с моделями с такими свойствами, почти наверняка будет иметь такие коллекции в виде таблиц или разного рода списков.
Итого, если допустить вероятность наличия коллекции и вероятность оформления её состояния через рекорд за 0.9, то вероятность необходимости рекорда будет равна 0.81 в случае свойств-ключей. Допустим, это половина всех кейсов номинативной типизации, то результативная вероятность необходимости рекордов будет равна 0.4, а это не то значение которое можно выразить через «очень редко».
Очень субъективные оценки, Серег :) Мне вот не кажется, что если у меня сложный фронт, то я именно по такого рода ключам буду рекорды составлять. Но в целом готов согласиться, что "очень редко" тоже слишком категорично было сказано :) Давай короче сойдёмся на том, что такие случаи бывают, но и не в 100%, поэтому для толстых клиентов в целом есть вероятность, что брендинг будет более полезен, чем костылен. А как минимум это уже хороший повод знать про эту тему. Согласен, что знать надо с учетом возможных минусов и костылей
Ещё не раскрыты сомнительные моменты с формами . На фронте, форма - источник ввода, требующий валидации в подавляющем большинстве случаев.
processBoots(bootsID: string)
? При хорошем именовании функций/переменных класс таких ошибок должен отсутствовать. Что ты имеешь ввиду под формами? Вьюху с компонентами-инпутами и тд?
Ага
Ну для вьюх, реакт-компонент и тд эта тема в целом менее релевантна. Я поэтому и говорю про толстый клиент, потому что там есть отдельный слой чистой доменной логики и как раз для него это всё релевантно, именно там я пытаюсь по максимуму в моделях все выражать на уровне типов и именно там мне нужна выразительность этих типов.
Нам при сабмите нужно получить уже затипизированные свойства.
тут не согласен, при сабмите мы должны получать сырой пользовательский ввод, и если нужно использовать смарт-конструкторы. Т.к. мы говорим, что смарт-конструктор не всякий сырой ввод сможет преобразовать в нужное нам значение (например, UUID из строки не всегда может получиться), то мы не можем в стейте формы хранить прошедшие валидацию данные вместо сырого ввода пользователя.
Но если у смарт-конструктора нет никаких правил и любая введенная юзером строка подходит, то можно легко написать Field (как минимум, в redux-form и в final-form), который на юзерский ввод будет применять к новому значению смарт-конструктор и полученное значение отправлять в стейт формы.
Нам при сабмите нужно получить уже затипизированные свойства.
тут не согласен, при сабмите мы должны получать сырой пользовательский ввод, и если нужно использовать смарт-конструкторы. Т.к. мы говорим, что смарт-конструктор не всякий сырой ввод сможет преобразовать в нужное нам значение (например, UUID из строки не всегда может получиться), то мы не можем в стейте формы хранить прошедшие валидацию данные вместо сырого ввода пользователя.
Я имел в виду что после сабмита при успешной валидации библиотека должна предоставить модель с уже затипизированными свойствами. В стейте формы разумеется не получится держать номинативно затипизированные свойства
Но если у смарт-конструктора нет никаких правил и любая введенная юзером строка подходит, то можно легко написать Field (как минимум, в redux-form и в final-form), который на юзерский ввод будет применять к новому значению смарт-конструктор и полученное значение отправлять в стейт формы.
Ну вот, ограничения и повторная валидация в конструкторе и в форме
блин, я вообще не понимаю как ты связал пользовательский ввод и конечное значение :) зачем нам второе, если ввод еще не закончен и в инпутах что-то не валидное?
ну типа второе не всегда можно вывести из первого, а если так, то наверное логично дождаться когда юзер закончит ввод и предоставит на вход валидную строку.
ну типа второе не всегда можно вывести из первого, а если так, то наверное логично дождаться когда юзер закончит ввод и предоставит на вход валидную строку.
ты правильно говоришь, я разве где-то утверждал обратное?
тут 2 пункт не об этом? https://github.com/fullstack-development/developers-roadmap/issues/143#issuecomment-574477524
Пропустил это сообщение. Понял что ты имеешь ввиду, но не понял зачем так переусложнять :)
Я имел в виду что после сабмита при успешной валидации библиотека должна предоставить модель с уже затипизированными свойствами. В стейте формы разумеется не получится держать номинативно затипизированные свойства
Я имел в виду что после сабмита при успешной валидации библиотека должна предоставить модель с уже затипизированными свойствами.
Ну вот сильное заявление, почему "должна"? Мне кажется, норм, что вьюха собирает данные и отдаёт в своём сыром виде, ты потом это мапаешь в уже в доменные модели и передаешь дальше по слоям приложения куда-нибудь ближе к ядру.
Начало обсуждения тут: https://github.com/fullstack-development/developers-roadmap/pull/140#discussion_r365026763