Do you have any suggestions how to validate values as unionize types? Here is a real world example of where this is needed.
I have a union type to represent a page modal.
const Modal = unionize({
Foo: ofType<{ foo: number }>(),
Bar: ofType<{ bar: number }>()
});
type Modal = typeof Modal._Union;
The modal is specified to the application through the URL as a query param, e.g. http://foo.com/?modal=VALUE, where VALUE is an object after it has been JSON stringified and URI encoded, e.g.
`?modal=${encodeURIComponent(JSON.stringify(Modal.Foo({ foo: 1 })))}`
// ?modal=%7B%22tag%22%3A%22Foo%22%2C%22foo%22%3A1%7D
In my application I want to be able to match against the modal using the match helpers provide by unionize. However, it is not safe to do so, because the modal query param could be a string containing anything, e.g. modal=foo.
For this reason I need to validate the value before matching against it.
In the past I've enjoyed using io-ts for the purpose of validating. I am aware you also have a similar library called runtypes.
If this is a common use case, I wonder if there's anything we could work into the library, or build on top of it, to make it easier.
Here is a working example that uses io-ts and tries to share as much as possible, but duplication is inevitable and it's a lot of boilerplate.
import { ofType, unionize } from "unionize";
import * as t from "io-ts";
import { option } from "fp-ts";
//
// Define our runtime types, for validation
//
const FooRT = t.type({ tag: t.literal("Foo"), foo: t.number });
type Foo = t.TypeOf<typeof FooRT>;
const BarRT = t.type({ tag: t.literal("Bar"), bar: t.number });
type Bar = t.TypeOf<typeof BarRT>;
const ModalRT = t.taggedUnion("tag", [FooRT, BarRT]);
//
// Define our unionize types, for object construction and matching
//
export const Modal = unionize({
Foo: ofType<Foo>(),
Bar: ofType<Bar>()
});
export type Modal = typeof Modal._Union;
//
// Example of using the unionize object constructors
//
const modalFoo = Modal.Foo({ foo: 1 });
//
// Example of validation with io-ts + matching with unionize
//
const parseJsonSafe = (str: string) => option.tryCatch(() => JSON.parse(str));
const validateModal = (str: string) => {
console.log("validating string:", str);
parseJsonSafe(str).foldL(
() => {
console.log("invalid json");
},
parsedJson => {
ModalRT.decode(parsedJson).fold(
() => console.log("parsed json, invalid modal"),
modal =>
Modal.match({
Foo: foo =>
console.log("parsed json, valid modal foo", foo),
Bar: bar =>
console.log("parsed json, valid modal bar", bar)
})(modal)
);
}
);
};
validateModal(JSON.stringify({ tag: "Foo", foo: 1 }));
/*
validating string: {"tag":"Foo","foo":1}
parsed json, valid modal foo { tag: 'Foo', foo: 1 }
*/
validateModal(JSON.stringify({ tag: "Bar", bar: 1 }));
/*
validating string: {"tag":"Bar","bar":1}
parsed json, valid modal bar { tag: 'Bar', bar: 1 }
*/
validateModal("INVALID JSON TEST");
/*
validating string: INVALID JSON TEST
invalid json
*/
Do you have any suggestions how to validate values as unionize types? Here is a real world example of where this is needed.
I have a union type to represent a page modal.
The modal is specified to the application through the URL as a query param, e.g.
http://foo.com/?modal=VALUE, whereVALUEis an object after it has been JSON stringified and URI encoded, e.g.In my application I want to be able to match against the modal using the match helpers provide by unionize. However, it is not safe to do so, because the
modalquery param could be a string containing anything, e.g.modal=foo.For this reason I need to validate the value before matching against it.
In the past I've enjoyed using io-ts for the purpose of validating. I am aware you also have a similar library called runtypes.
If this is a common use case, I wonder if there's anything we could work into the library, or build on top of it, to make it easier.
Here is a working example that uses io-ts and tries to share as much as possible, but duplication is inevitable and it's a lot of boilerplate.