A
arktype2mo ago
TheGuy

Custom message not working

Hi there, For whatever reason when going off creating a custom message when doing:
export const productBase = type({
name: type.string.configure({
message: (ctx) => `Product name is required.`,
})
});
export const productBase = type({
name: type.string.configure({
message: (ctx) => `Product name is required.`,
})
});
I am not seeing the above message and instead getting the default name must be a string (was missing) What is going wrong here? When drilling into the schema:
{
"domain": "string",
"meta": {
"message": "$ark.message"
}
}
{
"domain": "string",
"meta": {
"message": "$ark.message"
}
}
I would of expected this to be different. I have also tried just having message as a string and not a function.
7 Replies
TizzySaurus
TizzySaurus2mo ago
You'll get that error when a name is provided by not a string
TheGuy
TheGuyOP2mo ago
I still see this issue even when doing a string not empty
export const test = type({
name: type("string>0").configure({message: "oops"})
});
export const test = type({
name: type("string>0").configure({message: "oops"})
});
TizzySaurus
TizzySaurus2mo ago
My point is you'll get the custom error for productBase({name: 1234}) because 1234 isn't a string
TheGuy
TheGuyOP2mo ago
Sure. I understand. But I would expect to also see the message with the string example.
TizzySaurus
TizzySaurus2mo ago
I believe there was a way to do what you're after but I unfortunately don't remember it :blobsweats:
TheGuy
TheGuyOP2mo ago
Haha cheers. I think i figured out why. It turns out that with string>0 if you pass and undfined value it will hit that default case still
ssalbdivad
ssalbdivad2mo ago
This is a bit tricky. It's happening because technically, the error when a value is missing is occurring when the root of the object itself is checked, so it never even hits the value that has been configured. Here is the least convoluted way I could think of to solve this:
function missingToUndefined<o extends type.Any<Record<string, unknown>>>(
o: o
): o
function missingToUndefined(o: type<Record<string, unknown>>) {
const undefinedProps = Object.fromEntries(
o.props.map(prop => [prop.key, undefined])
)
return type.object.pipe(
actualProps => ({
...undefinedProps,
...actualProps
}),
o
)
}

export const productBase = missingToUndefined(
type({
name: type.string.configure({
message: ctx => `Product name is required.`
})
})
)

// Product name is required.
productBase.assert({})
function missingToUndefined<o extends type.Any<Record<string, unknown>>>(
o: o
): o
function missingToUndefined(o: type<Record<string, unknown>>) {
const undefinedProps = Object.fromEntries(
o.props.map(prop => [prop.key, undefined])
)
return type.object.pipe(
actualProps => ({
...undefinedProps,
...actualProps
}),
o
)
}

export const productBase = missingToUndefined(
type({
name: type.string.configure({
message: ctx => `Product name is required.`
})
})
)

// Product name is required.
productBase.assert({})
You could also use a generic approach like the one mentioned here to remove the extra wrapper function and accept a definition to transform directly: https://arktype.io/docs/generics#external That said, given this is still quite convoluted, I would definitely appreciate if you could open an issue for this so we can plan to implement a more elegant solution internally in the future!

Did you find this page helpful?