Configure errors in a scope

Is it possible to set a expected/actual/etc handler for every type in a scope? I have something like this:
const foo = type({
name: "string",
myKey: "number",
})
const fooArray = foo.array()

const validatedFooArray = fooArray([{ name: "fooName", myKey: "should be a number" }])
if (validatedFooArray instanceof type.errors) {
console.log(validatedFooArray.summary)
}
const foo = type({
name: "string",
myKey: "number",
})
const fooArray = foo.array()

const validatedFooArray = fooArray([{ name: "fooName", myKey: "should be a number" }])
if (validatedFooArray instanceof type.errors) {
console.log(validatedFooArray.summary)
}
The error summary looks as follows: value at [0].myKey must be a number (was a string) Ultimately what I want to do is tell the user the name of object that had the error (let's just assume the name property is always on the object). So something like: (for object with name 'foo'): value at [0].myKey must be a number (was a string) I was thinking of doing something like this:
.configure({
message: (ctx) => {
// if the path starts with an array index, prepend the message with the name
if (/^\[\d+\]\.$/.test(ctx.path.stringify())) {
const name = ctx.data[0].name
return `(for object with name '${name}': ${ctx.problem}`
} else {
return ctx.problem
}
.configure({
message: (ctx) => {
// if the path starts with an array index, prepend the message with the name
if (/^\[\d+\]\.$/.test(ctx.path.stringify())) {
const name = ctx.data[0].name
return `(for object with name '${name}': ${ctx.problem}`
} else {
return ctx.problem
}
But I would need to apply that to all types contained within my foo .
22 Replies
ssalbdivad
ssalbdivad5d ago
Not exactly the way you describe. The alias associated with the type wouldn't be exposed in error context and would be somewhat tricky to define precisely- is it the nearest named type from where the error occurred? You could create a namedErrorType helper or something that was like type but added this under the hood after the initial definition was parsed?
Genshii
GenshiiOP5d ago
What do you mean by named type?
ssalbdivad
ssalbdivad5d ago
A type defined at the top-level of a scope
Genshii
GenshiiOP5d ago
Oh gotcha, well in my case I don't think I need to reference the alias at all? I think everything I need is on the error ctx itself (at least for problem/message)
ssalbdivad
ssalbdivad5d ago
Oh sorry I should have looked closer when you mentioned a scope, I thought name was referring to what you named the type
Genshii
GenshiiOP5d ago
Yeah I don't think I even need to use a scope necessarily, it seemed like that was the right direction though I know I can set an onFail globally or for a scope, but that doesn't have the context I would need
ssalbdivad
ssalbdivad5d ago
Yeah that's not what you'd want You can configure this stuff at a scope-level, but you need to do it for each error type Depending on what kinds of errors you actually care about though, you might just need domain (typeof), unit (===)
Genshii
GenshiiOP5d ago
ahhhh okay is there anything in the docs that describe what domain, unit, predicate, etc refer to?
ssalbdivad
ssalbdivad5d ago
Not yet, there are a lot of error/node types and I haven't got that covered yet because most people don't even use scopes directly so it wouldn't matter. Hopefully most of them are somewhat intuitive though predicate is for a custom error/narrow function
Genshii
GenshiiOP5d ago
uhh sorry, trying to do this with a scope, but running into an issue where .array() doesn't exist on the exported module i thought a module was basically just a type, or at least could be used in he same way
ssalbdivad
ssalbdivad5d ago
A module is the collection of types you defined in your scope that's it You can see by using mod. and you'll see all your names
Genshii
GenshiiOP5d ago
const foo = type.module({
name: "string",
myKey: "number",
})
const fooArray = foo.array()
const foo = type.module({
name: "string",
myKey: "number",
})
const fooArray = foo.array()
a module doesn't have any of the normal type functions on it, do I need to somehow convert it to a type first? or maybe I'm misunderstanding what modules actually are
ssalbdivad
ssalbdivad5d ago
Modules are groups of types. It seems like you're just defining a normal type? If you do foo.name you will see string I guess that is kinda true for a type in this case also (although you can't accept props on an object type directly like that) But usually it would be something more like the examples from the docs:
const coolScope = scope({
// keywords are still available in your scope
id: "string",
// but you can also reference your own aliases directly!
user: { id: "id", friends: "id[]" },
// your aliases will be autocompleted and validated alongside ArkType's keywords
usersById: {
"[id]": "user | undefined"
}
})
const coolScope = scope({
// keywords are still available in your scope
id: "string",
// but you can also reference your own aliases directly!
user: { id: "id", friends: "id[]" },
// your aliases will be autocompleted and validated alongside ArkType's keywords
usersById: {
"[id]": "user | undefined"
}
})
Modules are just like scopes that come pre-exported
Genshii
GenshiiOP5d ago
right, my intention was to then set the error handlers on domain etc for that module
ssalbdivad
ssalbdivad5d ago
You'd just pass the config to the second param But you could not do .array(). It doesn't make sense to convert a group of types to an array You could do foo.myKey.array()
Genshii
GenshiiOP5d ago
ohh i see, i thought modules defined an object type, but I realize that's not the case at all what I'm really trying to do is this I think:
const foo = type.module({
bar: {
name: "string",
myKey: "number",
},
})
const fooArray = foo.bar.array()
const foo = type.module({
bar: {
name: "string",
myKey: "number",
},
})
const fooArray = foo.bar.array()
ssalbdivad
ssalbdivad5d ago
Yeah
Genshii
GenshiiOP5d ago
well i suppose then i'm back to square one lol, because error handlers only apply shallowly anyway
ssalbdivad
ssalbdivad5d ago
That's not how it works in scopes
Genshii
GenshiiOP5d ago
Dumb example but are saying that if I do something like this:
const foo = type.module(
{
bar: {
name: "string",
myKey: "number",
},
},
{
domain: {
message: (ctx) => {
console.log(ctx)
return "foo"
},
},
},
)
const foo = type.module(
{
bar: {
name: "string",
myKey: "number",
},
},
{
domain: {
message: (ctx) => {
console.log(ctx)
return "foo"
},
},
},
)
it would show "foo" for every validation failure on bar?
ssalbdivad
ssalbdivad5d ago
Yeah It's totally different internally When you apply the config to a specific type, you're changing a bunch of type nodes to have a structure that includes the custom error When you apply the config to a scope, it changes what default error logic is used if the node doesn't have custom logic at the time the error actually occurs
Genshii
GenshiiOP5d ago
gotcha, I'll play around with this, I think I'm on the right track thanks!

Did you find this page helpful?