valita icon indicating copy to clipboard operation
valita copied to clipboard

v.literal should accept spread arguments

Open Geordi7 opened this issue 4 years ago • 4 comments

Today:

const T = v.union(v.literal('a'), v.literal('b'), v.literal('c'));

Tomorrow:

const T = v.literal('a', 'b', 'c');

Geordi7 avatar Oct 20 '21 08:10 Geordi7

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.

marvinhagemeister avatar Oct 22 '21 21:10 marvinhagemeister

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'));

Geordi7 avatar Feb 01 '23 17:02 Geordi7

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 avatar Jul 28 '23 16:07 dimatakoy

@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.

Geordi7 avatar Feb 14 '24 16:02 Geordi7