Comments 11
А можно эту логику поместить в базовый класс Serializable
и в нем пробегать по ключам this?
Думаю, можно, но тогда потеряется гибкость с возможностью исключения свойств, которые не должны попадать в сериализованный объект. Вообще мне идея с абстрактным классом чисто для типизации не очень нравится. Подумаю как-нибудь над решением без него. Если получится, обновлю материал.
Чтобы исключать свойства можно их декорировать, еще можно кастомный сериализатор добавлять:
class Vehicle extends Serializable<SerializedVehicle>
{
@notSerialize()
private field: string;
@serialize(x => x.toISOString())
private createdAt: Date;
}
Кстати, какие декораторы используете, legacy или из TC39? С новыми периодически проблемы, что они не везде поддерживаются, или поддерживаются но по-разному, а старые рано или поздно выпилят.
Тут legacy, пока не выпилили. На TC39 не слишком трудно переделать будет. По поводу декорирования свойств видел решение, показалось труднее читать будет, искать по коду, плюс больше писанины. Хотя, это дело вкуса. В принципе, если над универсальным решением задуматься, можно и с декорированием свойств сделать, и с декорированием класса. С прокидыванием кастомного сериализатора хорошая мысль.
Исключить из результата сериализации некоторые свойства можно с помощью дополнительных параметров для декоратора, обернув его в функцию, которая принимает список параметров переменного количества
А вы в курсе, что нативная реализация JSON.stringify()
вторым параметром принимает replacer, который может быть callback, исключающий свойства из объекта? Ваша реализация вместе с Proxy()
выглядит куда более нагроможденной. Вместо Proxy()
можно использовать Object.assign({}, object)
, а затем, используя delete
, явно удалить ключи, которые не должны попадать в сериализацию. Это было бы нагляднее, понятнее и визуально короче - но это слишком "по-яваскриптовски".
А так, на первый взгляд, если не мучать себя ограничениями TS, весь код статьи можно оформить как небольшой однострочник использованияJSON.stringify()
.
Вообще, Proxy нужен, чтобы ловить обращение к методу serialize(), который отсутствует в исходном объекте. Пока не могу понять, как тут Object.assign() поможет. Создавать из исходного новый объект с методом serialize(), а в методе уже опять использовать Object.assign(), чтобы скопировать исходный объект и удалить "лишние" ключи? С JSON.stringify() интересная мысль. Но на выходе надо ещё JSON.parse() сделать, плюс опять же с коллекциями разобраться, так что не выйдет однострочник, наверное. Попробую упростить с этим, если решение проще будет рабочее, выложу.
Если заменить Proxy на Object.assign(), код получается лаконичнее, спасибо, доработал в статье. Но использовать Object.assign() для самой сериализции -- не очень идея, по-моему. Нам же нужно исключить ещё и функции, вычисляемые свойства тоже может потребоваться не сериализовать, так что там просто с delete выйдет та же сложность. Про JSON.stringify() -- будут получаться некорректные данные на выходе в случае undefined и всяких NaN, Infinity. Вот тут подробности: https://medium.com/@pmzubar/why-json-parse-json-stringify-is-a-bad-practice-to-clone-an-object-in-javascript-b28ac5e36521 .
Из того, что использовал, может пригодится:
Сериализация в JSON (похоже на Java Jackson): typestack/class-transformer
Валидация типа (схемы) сериализованного JSON: zod
Оба инструмента работают и задачу выполняют. В обоих нужно добавлять минимум кода для работы фреймворка. Оба довольно гибки.
Он они подходят не всегда. Есть и минусы:
Zod - требует описывать тип объекта на "своем языке", который используется для валидации и вывода TypeScript типа.
class-transformer'у нужно проставить декораторы в классе сущности.
честно говоря выглядит ооооооочень монструозно.
"которая отображает на пользовательский интерфейс создание или редактирование сущности с большим количеством взаимозависимых свойств" - это какого размера и сложности должна быть форма , что бы оправдать такой подход?
Сериализация сущностей с помощью декораторов на TypeScript