[Question] Can I validate one property value based on the values of other properties?
maddiehosseini opened this issue ยท 2 comments
maddiehosseini commented
๐ Feature request
Current Behavior
I have a schema defined like this:
export const Contact = t.type({
firstName: t.union([
withMessage(
tt.NonEmptyString,
() => ContactValidationErrorType.DataRequired
),
t.undefined,
]),
lastName: t.union([
withMessage(
tt.NonEmptyString,
() => ContactValidationErrorType.DataRequired
),
t.undefined,
]),
phoneNumber: t.union([
withMessage(
validatePhoneNumber,
() => ContactValidationErrorType.DataRequired
),
t.undefined,
]),
});
Desired Behavior
I would like to apply this rule: "at least one of the name fields should have value".
Who does this impact? Who is this for?
TypeScript users
Describe alternatives you've considered
I can do the name validation in a separate function and have a 2-steps validation, but I prefer not to do it. Because other team members may later forget to use it and miss part of the validation logic.
Your environment
Software | Version(s) |
---|---|
io-ts | 2.2.13 |
TypeScript | 4.7.4 |
mlegenhausen commented
You can define via a union that only firstName
or lastName
or both are required.
export const Contact = t.intersection([
t.union([
t.type({ firstName: tt.NonEmptyString }),
t.type({ lastName: tt.NonEmptyString }),
t.type({
firstName: tt.NonEmptyString,
lastName: tt.NonEmptyString
})
]),
t.partial({
phoneNumber: validatePhoneNumber
})
]);
maddiehosseini commented
Thank you, using the intersection
and partial
solved the problem ๐
I modified your solution a bit so it's easier to understand for other devs who read the code later:
export const Contact = t.intersection([
t.union([
firstNameShouldHaveValue,
lastNameShouldHaveValue,
nickNameShouldHaveValue
]),
t.partial({
phoneNumber: validatePhoneNumber
}),
]);
const firstNameShouldHaveValue = t.type({
firstName: withMessage(
tt.NonEmptyString,
() => ContactValidationErrorType.AllNameFieldsAreEmpty
),
lastName: t.union([
withMessage(
t.string,
() => ContactValidationErrorType.InvalidCompanyName
),
t.undefined,
]),
nickName: t.union([
withMessage(
t.string,
() => ContactValidationErrorType.InvalidNickName
),
t.undefined,
]),
});
const lastNameShouldHaveValue = t.type({
firstName: t.union([
withMessage(t.string, () => ContactValidationErrorType.InvalidFirstName),
t.undefined,
]),
lastName: withMessage(
tt.NonEmptyString,
() => ContactValidationErrorType.AllNameFieldsAreEmpty
),
nickName: t.union([
withMessage(
t.string,
() => ContactValidationErrorType.InvalidNickName
),
t.undefined,
]),
});
const nickNameShouldHaveValue = t.type({
firstName: t.union([
withMessage(t.string, () => ContactValidationErrorType.InvalidFirstName),
t.undefined,
]),
lastName: t.union([
withMessage(
t.string,
() => ContactValidationErrorType.InvalidLastName
),
t.undefined,
]),
nickName: withMessage(
tt.NonEmptyString,
() => ContactValidationErrorType.AllNameFieldsAreEmpty
),
});