io-ts
io-ts copied to clipboard
Validate array contains one of each object with specific properties and values
I looked at the docs but cant find the validation I need.
I have an array of object literals each with specific properties and values.
I want to validate that comboItems is not empty and contains a min and max of 3 objects AND that there is an itemType === 'Entree' AND an itemType==='Side' AND an itemType==='Beverage'
import * as t from "io-ts";
import { pipe } from 'fp-ts/function'
import { fold, isRight, isLeft } from 'fp-ts/Either'
enum ItemTypeEnum {'MEALS_GROUP'='MEALS_GROUP', 'ENTREES'='ENTREES', 'SIDES_GROUP'='SIDES_GROUP', 'BEVERAGES_GROUP'='BEVERAGES_GROUP'}
const Entree = t.type({
itemType: t.literal(ItemTypeEnum.ENTREES),
})
const Side = t.type({
itemType: t.literal(ItemTypeEnum.SIDES_GROUP),
})
type Entree = t.TypeOf<typeof Entree>
type Side = t.TypeOf<typeof Side>
export const Meal = t.type({
comboItems: t.readonlyArray(t.union([Entree, Side]))
})
This shows as a success (which is expected):
console.log(pipe(
Meal.decode({comboItems: [{itemType: ItemTypeEnum.ENTREES}, {itemType: ItemTypeEnum.SIDES_GROUP}]}),
fold(
// failure handler
(errors) => `error: ${JSON.stringify(errors)}`,
// success handler
(a) => `success: ${JSON.stringify(a)}`
)
)
)
But this also shows as a success, which should not because it's missing {{itemType: ItemTypeEnum.SIDES_GROUP}
console.log(pipe(
Meal.decode({comboItems: [{itemType: ItemTypeEnum.ENTREES}]}),
fold(
// failure handler
(errors) => `error: ${JSON.stringify(errors)}`,
// success handler
(a) => `success: ${JSON.stringify(a)}`
)
)
)
Thanks for any help!
comboItems is not empty and contains a min and max of 3 objects
So `comboItems should contain exactly 3 objects?
If possible go with a tuple(Entree, Side, Beverages)
. The disadvantage is that now the order of the elements is important.
If I misunderstood your first assumption and you mean at least 3 items. I would use a TypeScript Type that can describe your constraints then you are able to describe it in io-ts
.
import { NonEmptyArray } from 'fp-ts/NonEmptyArray'
interface Meal {
ENTREES: NonEmptyArray<Entree>
SIDES_GROUP: NonEmptyArray<Side>
BEVERAGES_GROUP: NonEmptyArray<Beverage>
}
function comboItems(meal: Meal): NonEmptyArray<Entree | Side | Beverage> {
return [...meal.ENTREES, ...meal.SIDES_GROUP, ...meal.BEVERAGES_GROUP]
}
If you input is an array you can convert it to a record with groupBy
from NonEmptyArray
. Then I would check that your record is equal to the Meal
interface e. g. by checking with an io-ts
definition of Meal
and the is
method.
I would use a branded complex type.
https://github.com/gcanti/fp-ts/issues/1340