docs: add some number and date validations helpers
Author: userquinCreated Sep 8, 2026Updated Sep 9, 2026
Following with #1494 here some useful code you can add to the docs (I'm using vuetify 4 + valibot + vee-validate + vue-i18n with nuxt 4, check screenshots at the end).
requiredIntegerexport function requiredInteger(
t: (key: string, ...args: any[]) => string,
min?: number,
max?: number
) {
return v.pipe(
v.nonOptional(
v.union([v.string(), v.number()]),
() => t('messages.required')
),
v.transform((input) => {
if (typeof input === 'string' && input.trim() === '') {
return Number.NaN
}
return Number(input)
}),
v.number(() => t('messages.required')),
v.finite(() => t('messages.required')),
v.integer(() => t('messages.invalid')),
v.minValue(typeof min === 'number' ? min : Number.MIN_SAFE_INTEGER, ({ requirement }) => t('messages.min', { min: requirement })),
v.maxValue(typeof max === 'number' ? max : Number.MAX_SAFE_INTEGER, ({ requirement }) => t('messages.max', { max: requirement })),
)
}export function requiredDouble(
t: (key: string, ...args: any[]) => string,
min?: number,
max?: number
) {
return v.pipe(
v.nonOptional(
v.union([v.string(), v.number()]),
() => t('messages.required')
),
v.transform((input) => {
if (typeof input === 'string' && input.trim() === '') {
return Number.NaN
}
return Number(input)
}),
v.number(() => t('messages.required')),
v.finite(() => t('messages.required')),
v.minValue(typeof min === 'number' ? min : Number.MIN_SAFE_INTEGER, ({ requirement }) => t('messages.min', { min: requirement })),
v.maxValue(typeof max === 'number' ? max : Number.MAX_SAFE_INTEGER, ({ requirement }) => t('messages.max', { max: requirement })),
)
}export function optionalInteger(
t: (key: string, ...args: any[]) => string,
min?: number,
max?: number
) {
return v.optional(
v.union([
v.literal(''),
v.pipe(
v.nonOptional(
v.union([v.string(), v.number()]),
() => t('messages.required')
),
v.transform((input) => {
if (typeof input === 'string' && input.trim() === '') {
return Number.NaN
}
return Number(input)
}),
// Valida que el dato entrante sea estrictamente un tipo number
v.number(() => t('messages.required')),
v.finite(() => t('messages.required')),
v.integer(() => t('messages.invalid')),
v.minValue(typeof min === 'number' ? min : Number.MIN_SAFE_INTEGER, ({ requirement }) => t('messages.min', { min: requirement })),
v.maxValue(typeof max === 'number' ? max : Number.MAX_SAFE_INTEGER, ({ requirement }) => t('messages.max', { max: requirement })),
),
]),
'',
)
}export function optionalDouble(
t: (key: string, ...args: any[]) => string,
min?: number,
max?: number
) {
return v.optional(
v.union([
v.literal(''),
v.pipe(
v.nonOptional(
v.union([v.string(), v.number()]),
() => t('messages.required')
),
v.transform((input) => {
if (typeof input === 'string' && input.trim() === '') {
return Number.NaN
}
return Number(input)
}),
v.number(() => t('messages.required')),
v.finite(() => t('messages.invalid')),
v.minValue(typeof min === 'number' ? min : Number.MIN_SAFE_INTEGER, ({ requirement }) => t('messages.min', { min: requirement })),
v.maxValue(typeof max === 'number' ? max : Number.MAX_SAFE_INTEGER, ({ requirement }) => t('messages.max', { max: requirement })),
),
]),
'',
)
}export function isValidIsoDate(t: (key: string, ...args: any[]) => string) {
return v.check(
(input: string) => {
if (input.length === 0) {
return true
}
const [yearStr, monthStr, dayStr] = input.split('-')
if (!yearStr || !monthStr || !dayStr) {
return true
}
const year = +yearStr
const month = +monthStr
const day = +dayStr
if (Number.isNaN(year) || Number.isNaN(month) || Number.isNaN(day)) {
return true
}
const date = new Date(Date.UTC(year, month - 1, day))
return (
date.getUTCFullYear() === year &&
date.getUTCMonth() === month - 1 &&
date.getUTCDate() === day
)
},
() => t('messages.invalid'),
)
}export function requiredDate(
t: (key: string, ...args: any[]) => string,
) {
return v.optional(
v.pipe(
v.string(),
v.nonEmpty(() => t('messages.required')),
v.isoDate(() => t('messages.invalid')),
isValidIsoDate(t),
),
'',
)
}export function optionalDate(
t: (key: string, ...args: any[]) => string,
) {
return v.optional(
v.union([
v.literal(''),
v.pipe(
v.string(),
v.isoDate(() => t('messages.invalid')),
isValidIsoDate(t),
),
]),
'',
)
}valibot schema using previous helpers
valibot messages on vuetify v-textfile (type number) after clicking the submit button
Source: open-circle/valibot