v.literal should accept spread arguments
Today:
const T = v.union(v.literal('a'), v.literal('b'), v.literal('c'));
Tomorrow:
const T = v.literal('a', 'b', 'c');
I can see how it makes the code more concise, but I'm not sure about moving away from the explicitness valita currently offers. I like that currently a literal type is always a literal type and not a magic union + literals.
how about calling it literals ?
import * as v from '@badrap/valita';
const A = v.literals('a', 'b');
// equivalent to
const B = v.union(v.literal('a'), v.literal('b'));
I personally dislike it, current .union is much more generic, than function that can only handle strings. You can easily make own helper for this single case.
const TYPES = {
first: '__first',
second: '__second',
} as const;
function fromConst<T extends string>(input: Record<string, T>) {
const literals = Object.values(input).map((type) => v.literal(type));
return v.union(...literals);
}
const types = fromConst(TYPES);
type x = v.Infer<typeof types>;
// ^ '__first' | '__second'
@dimatakoy - your example doesn't really capture my intent.
This is what I add to my projects that use valita:
export const vLiterals = <L extends string | boolean | number>(...literals: L[]) =>
v.union(...literals.map(l => v.literal(l))) as v.Type<L>
const vOneOrHello = vLiterals(1,'hello');
type OneOrHello = v.Infer<typeof vOneOrHello> // 1 | 'hello'
it works as you'd expect, this is a QOL change for valita, not a whole new type parser, under the hood it's just a union parser of literal parsers, which you would have to write anyway. I'm suggesting that valita includes it because it's simple and useful.