Open is-jonreeves opened 1 month ago
On a related note, NaN
doesn't play well with .value
or .notValue
...
This is more of a JS issue though, as NaN === NaN
is always false. Dunno if its a scenario worth handling, but had me scratching my head for a moment. I'm just using v.check(i => !Number.isNaN(i), () => I18n.t('validation.notNumber'))
instead.
Thanks for sharing that idea. Yes, we can add values
and notValues
. Feel free to create a PR. Instead of using values
you may want to consider using picklist
or literal
with union
first as it leads to a more precise TypeScript type.
Can you share your entire !NaN schema? v.number()
already excludes NaN
for you.
Can you share your entire !NaN schema? v.number() already excludes NaN for you.
This likely occurred for me because I'm also allowing string
as a valid input and coercing to number
. The intention here was to first conform every valid input to the expected type allowing them to share the same validation logic:
v.pipe(
// Input
v.union([
// Expected (intended "input" type)
v.number(),
// Coerced (supported alternative "input" types)
v.pipe(v.string(), v.trim(), v.transform(i => Number(i || NaN))),
]),
// Validation
// v.notValue(NaN, () => 'Not a Number'), // <--- couldn't use this
v.check(i => !Number.isNaN(i), () => 'Not a Number'),
v.integer(() => 'Not an Integer'),
v.minValue(1, () => 'Too Small'),
v.maxValue(65535, () => 'Too Large'),
);
The tricky thing with Number(string)
is that an empty string evaluates to 0
, so it makes more sense to force blank to NaN
. I could instead force to null
, but the number validations would then misbehave.
The bigger picture is that I was trying to recreate a Zod usage we had, where we needed "optional numbers", and I recalled it being a bit of a pain... Essentially, a form input (string) where the value should be coerced to a number, is within a range, but can also be optional (with no fallback). Here is an example:
import * as v from 'valibot';
/** Schemas */
const AsNumberSchema = v.union([
// Expected (intended "input" type)
v.number(),
// Coerced (supported alternative "input" types)
v.pipe(v.string(), v.trim(), v.transform(i => Number(i || NaN))),
]);
const AsUndefinedSchema = v.union([
// Optional (coercing null as undefined)
v.pipe(v.null(), v.transform(() => undefined)),
v.undefined(),
]);
const PortSchema = v.pipe(
// Supported Types
AsNumberSchema,
// Validation
v.check(i => !Number.isNaN(i), () => 'Not a Number'),
v.integer(() => 'Not an Integer'),
v.minValue(1, () => 'Too Small'),
v.maxValue(65535, () => 'Too Large'),
);
const ConfigurationSchema = v.object({
port: v.union([ PortSchema, AsUndefinedSchema ]),
});
/** Types */
type ConfigurationSchema = v.InferOutput<typeof ConfigurationSchema>;
/** Debug */
const result = v.safeParse(ConfigurationSchema, { port: undefined }, { abortPipeEarly: true });
console.log(result);
There might be a better (more repeatable) approach to this, as I would also need to do similar string/undefined support for booleans (who don't have an "invalid" state to work with). So maybe using null
could make more sense, but I'd need to find a way to restructure it to bypass these validations.
Alternatively, perhaps the better option is to just create a separate dedicated Form/UserInput version of the schemas (that always assumes a string as input) rather than one that caters for everything. I was just trying to avoid repetition, but potentially separating out these concerns makes more sense.
Instead of using values you may want to consider using picklist or literal with union first as it leads to a more precise TypeScript type.
With the above in mind, the scenario I was curious about, was if I needed to exclude a set of reserved ports from those available. If it was always a non-contiguous list then something like .notValues([], ...)
would have been handy, but in this case its more likey to be a collection of ranges, so a custom .check
probably makes more sense anyways. It just occurred to me that a whitelist/blacklist validation could be handy in other situations.
Yes, let's keep this open. Maybe someone in the community will implement it, or I will do it at a later date.
I was looking through the validators to see if there was an easy way to blacklist or whitelist a set of strings/numbers (all in one go). I see that
.value
and.notValue
are there, but didn't see any ones that would allow for an array of items to check against.I'm aware this could be done with a custom
.check
or.regex
, but wondered if it was a common enough usecase where we might consider adding.values
and.notValues
?