I've finally decided to upgrade from Yup 0.29
to 1.2
and faced some issues with it's types. I'm Looking for help to find a best solution for typing yup schemas.
In 0.29
quite universal type Schema
used to fit everywhere, but now it doesn't.
Here I Expect for compiler to complain, because Yup Object Schema doesn't satisfy my Generic type, how do I make this schema to be invalid because of MyType? Also to be more strict, it should complain when field is not optional, but .required()
or .nullable()
is not set.
interface MyType {
id: number;
name: string;
}
const mySchema = yup.object<MyType>({
id: yup.number().required(),
// name: yup.string().required(), <--- Here compiler used to raise an error in 0.29
}).required()
Edit: First issue is solved, found an explanation here
Here I have components and functions, where I have an argument - a field schema (string/number/object, etc)
interface MyType {
id: number;
name: string;
}
const mySchema = yup.object<MyType>({
id: yup.number().required(),
name: yup.string().required(),
}).required()
const idSchema = yup.reach(mySchema, "id");
const nameSchema = yup.reach(mySchema, "name");
function validateField(fieldSchema: Schema) {
fieldSchema.validate("TEST")
}
validateField(idSchema) // <--- Here compiler complains, where in 0.29 it didn't
I'm not quite sure about this Reference | ISchema
type, cannot find Reference type in Yup exported interfaces.
Per this PR, which was made almost a year ago, returning a type of Reference
and ISchema
is supposed to make the types more intelligent. You'll see that reach
internally makes use of getIn
, which goes through the schema to return either an ISchema
or a Reference
, this is better because when one defines a schema, they might have a Reference
field using ref
-
import { ref, object, string } from 'yup';
let schema = object({
baz: ref('foo.bar'),
foo: object({
bar: string(),
}),
x: ref('$x'),
});
schema.cast({ foo: { bar: 'boom' } }, { context: { x: 5 } });
// => { baz: 'boom', x: 5, foo: { bar: 'boom' } }
And the ref
creates an object of type (you guessed it), Reference
. Note that the other types such as object
, number
etc., all extends Schema
(parsed recursively), but that's not the case for a reference created by ref
, so, when you're trying to reach
and you have a schema like the one showed above, you might get a Reference
or Schema
, and hence the added types are better.
You can make use of an assertion utility isSchema
, provided by the library, like so -
import yup, { isSchema, ISchema, ref } from "yup";
type Reference<TValue = unknown> = ReturnType<(typeof ref<TValue>)>;
interface MyType {
id: number;
name: string;
}
const mySchema = yup.object<MyType>({
id: yup.number().required(),
name: yup.string().required(),
}).required();
const idSchema = yup.reach(mySchema, "id");
const nameSchema = yup.reach(mySchema, "name");
function validateField<T>(field: ISchema<T> | Reference) {
if (isSchema(field)) return field.validate("TEST");
throw new Error("Can't validate a Reference without a schema");
// Or add your own implementation for validating reference
// you can modify signature of validateField for that
}
validateField(idSchema); // no error
validateField(nameSchema); // no error
Here's a Playground link.