Open thany opened 1 year ago
Warning: use it on your own risk, I don't know if the proposed solution will be applicable for the future versions of YUP.
This is what I use in the current project, which uses pretty outdated YUP version.
Every schema test method recieves an error message. The type of the message:
export type Message<Extra extends Record<string, unknown> = any> =
| string
| ((params: Extra & MessageParams) => unknown)
| Record<PropertyKey, unknown>;
The function case is the escape hatch
I use. A message might be a function, which recieves validation related parameters, like requried length for yup.string().length(...) for example, and it returns unknown
. So YUP makes no assumptions about what to do this the returned value and thus returns it as is in the exception which will be thrown by YUP schema validate() method. In my case I provide YUP tests with a function similar to the code below:
const defferError = <TDeferredError extends string>(errorType: TDeferredError) => <TExtra extends Record<string, unknown>(params: Extra & MessageParams) => ({
errorType,
...params
})
and use it like this:
const stringSchema = yup.string().required(defferError('required'))
in the exception throw by sctringSchema.validate
objects returned by defferError('required')
resulting function will be present as is - just error type + parameters YUP provides. So you can analyze it and then format according to whatever locale you need.
I expect that passing a function as an error message will continue to exist, and it could indeed be used as a way to defer building the actual string messages. But it doesn't solve the underlying problem: a schema, once built, is effectively immutable and no functions exist to transform it with extended information like labels or messages separately from the validation rules.
My experience shows that to defer errors is enough to build any message you might need later. I might have a schema like:
const formSchema = yup.objectSchema({
field: yup.string.required(deferError('required'))
})
Producing a ValidationException with inner error:
{
errorType: 'required',
path: 'field'
originalValue: undefined
value: undefined,
label: 'field'
}
Which I can easily format to:
[Some real field name] is requried
To defer an error message allows you to create schema to validate an entity, as well as to make decision regarding how defered error type relate to acutal fields later at the validation schema applicaton time.
Can't do the same with labels, right?
As far as I get it, the point of labels is to substitute schema fields with human readable names in the string type error messages, so I see no reason one needs labels if he/she controls entire message.
And if one does not control the entire message (I guess you meant schema)?
Labels of fields might be supplied by a different part of the system, somewhere "far away" from the schema. And maybe I can't just pass the labels down into whereever the schema gets built - one of the pet peeves, I guess, of applications that are slightly more complicated than simple examples from a getting started guide.
Yup's label is really meant for simple cases, for complex or really dynamic messages I'd suggest using message keys or objects and handle the message rendering at validation like: https://github.com/jquense/yup#localization-and-i18n
It would be convenient if labels, normally passed through the
label()
function into a schema, could be passed into a schema separately. This would make it more convenient when loading in labels from a separate source like, say, a JSON file. Same for error messages.So imagine a simple schema like this:
This is great. This schema only contains the functional validation requirements, and no text content. Text content is fetched from somewhere else, all at once, through an object. Could be anything, so please try not to solve where our content is coming from. It comes from a CMS and there's nothing I can do about it.
I also don't want to hardcode error messages in every field in every schema. I want to dynamically construct a set of labels and error messages and pour that into a schema in a function that is generic and separate from schema creation.
It boils down to this: I want to add labels and error messages after having created the schema. Or before. But I can't have it intermixed with the schema as it is.
So what if I want to do something like this:
Now as for error messages:
There are 2
required
fields. One is for all field, for only the current schema, but not the entire application. This is currently not possible at all, afaik. The one insideage
is specific to theage
field.Please note that
setLocale
is global to the entire application, so it cannot be used to cover only a single schema.With this, those messages & labels objects can be created dynamically, and the schema creation can remain mean and lean, maybe a little bit like this. Those
withLabels
andwithMessages
would be some kind of functionality (written by me) that somehow enriches the passed schema with labels and messages:This leaves us with three levels of error messages:
setLocale
functionThere's not much use in proving a runnable reproduction case, since I just simply haven't found out how or where to do this in the current latest version. It just simply seems to be impossible to pass labels and messages into a schema after having created it.