Migrating from `io-ts` to `@effect/schema` question
floydspace opened this issue · comments
Hello guys, and thank you for your great work.
I got some time to explore this library. And currently stuck with understanding how to define the following scenario with fictional example:
const Circle = pipe(
S.struct({ radius: S.number, _isVisible: S.boolean }),
S.transform(?)
);
// decoding
expect(S.decode(Circle)({ radius: 10, _isVisible: true })).toEqual({
radius: 10,
_isVisible: true
});
// encoding
expect(S.encode(Circle)({ radius: 10, _isVisible: true })).toEqual({
radius: 10
});
So the idea is to have an ability to define a certain flag in an object and validate it with the decoder, but omit it during encoding.
This can be easily achieved by io-ts experimental codecs liek this:
const CircleDecoder = D.struct({
radius: D.number,
_isVisible: D.boolean,
});
type CircleDecoder = D.TypeOf<typeof CircleDecoder>;
type CircleEncoder = Omit<CircleDecoder, "_isVisible">;
const CircleEncoder: E.Encoder<CircleEncoder, CircleDecoder> = {
encode: (e) => ({
radius: e.radius,
}),
};
const Circle = C.make(CircleDecoder, CircleEncoder);
// decoding
expect(Circle.decode({ radius: 10, _isVisible: true })).toEqual({
_tag: 'Right',
right: { radius: 10, _isVisible: true }
});
// encoding
expect(Circle.encode({ radius: 10, _isVisible: true })).toEqual({
radius: 10
});
or I'm using codecs wrong, and they must obey the encode(decode(obj)) === decode(encode(obj))
law?
they must obey the encode(decode(obj)) === decode(encode(obj)) law?
That's not a hard law, just something you usually want.
import * as S from '@effect/schema/Schema'
import * as E from '@effect/data/Either'
const From = S.struct({ radius: S.number, _isVisible: S.optional(S.boolean) })
const To = S.struct({ radius: S.number, _isVisible: S.boolean })
const Circle = S.transformEither(From, To, S.decodeEither(To), ({ _isVisible, ...rest }) => E.right(rest))
console.log(S.decode(Circle)({ radius: 10, _isVisible: true })) // { _isVisible: true, radius: 10 }
console.log(S.decode(Circle)({ radius: 10 })) // throws
console.log(S.encode(Circle)({ radius: 10, _isVisible: true })) // { radius: 10 }
Thank you @gcanti it works, if I may I have a subsequent question:
if I need to apply the discriminant to the Circle in your example it fails with "extend" can only handle type literals or unions of type literals
const From = S.struct({ radius: S.number, _isVisible: S.optional(S.boolean) });
const To = S.struct({ radius: S.number, _isVisible: S.boolean });
const Circle = pipe(
S.transformEither(From, To, S.decodeEither(To), ({ _isVisible, ...rest }) =>
E.right(rest)
),
S.attachPropertySignature("_tag", "Circle")
);
looks like a bug, here
Line 775 in 780e634
-pipe(schema, extend(struct({ [key]: literal(value) })))
+pipe(to(schema), extend(struct({ [key]: literal(value) })))
I'll fix asap
amazing, thank you.
Patch released