I am creating a sectional form. Each section must have initial values, and a validation schema. To help the team develop in a consistent way, I want to make sure that each section
looks like this:
interface Section {
initialValues: { [key: string]: any };
validationSchema: { [key: string]: Yup.AnySchema };
}
Is there a way to constrain initialValues
and validationSchema
so that they must contain the same keys?
I.e., this will work:
const section1: Section = {
initialValues: {
name: "",
age: undefined // will be a number
},
validationSchema: {
name: Yup.string().required(),
age: Yup.number().required()
}
}
Whereas this would break:
const section1: Section = {
initialValues: {
name: "",
age: undefined // will be a number
},
validationSchema: {
name: Yup.string().required(),
height: Yup.number().required() // should error, as height does not exist on initialValues
// error because we are missing the age property and its validation here
}
}
Is there a way to accomplish such a constraint in typescript, without having to predefine the keys?
It is impossible, in this case, to make standalone type with negation. In order to make it work, in typescript, you usually need to use function arguments inference. See example:
import Yup from 'yup'
interface Section<T, U extends T> {
initialValues: T;
validationSchema: U;
}
const sectionBuilder = <T, U extends T>(section: Section<T, T & U>) => {
}
sectionBuilder({
initialValues: { name: 'hello' },
validationSchema: { name: 'john' } // ok
})
sectionBuilder({
initialValues: { name: 'hello' },
validationSchema: { age: 42 } // expected error
})
sectionBuilder({
initialValues: { name: 'hello' },
validationSchema: { name: 'john', age: 42 } // expected error
})
Generic U
in Section
is an intersection of T
and U
or in other words, U
is a set of properties which are common for initialValues
and validationSchema