A
arktype2mo ago
mikolaj

Matching on custom types

I have an array of errors (typed as any[]) that I need to transform. I’d like to break the transformation into several match cases. I’ve already done this with the Fluent API, but I’m not sure if the same approach works with the string-based API.
const FieldError = ({ title }: FieldErrorProps) => {
const field = useFieldContext();
const errors = field.state.meta.errors.flatMap((error: unknown) => {
const parser = match
.case(type.instanceOf(ArkErrors), (e) =>
e.flatMap((arkError) =>
arkError.hasCode('intersection')
? arkError.errors.map((arkErrorError) => arkErrorError.message)
: [arkError.message]
)
)
.case(type.instanceOf(ArkError), (e) =>
e.hasCode('intersection')
? e.errors.map((arkErrorError) => arkErrorError.message)
: [e.message]
)
.case("Error", (e) => [e.message])
.default((e) => [e?.toString() ?? 'Unknown error']);
return parser(error);
});
return <ErrorAlert title={title} errors={errors} />;
};
const FieldError = ({ title }: FieldErrorProps) => {
const field = useFieldContext();
const errors = field.state.meta.errors.flatMap((error: unknown) => {
const parser = match
.case(type.instanceOf(ArkErrors), (e) =>
e.flatMap((arkError) =>
arkError.hasCode('intersection')
? arkError.errors.map((arkErrorError) => arkErrorError.message)
: [arkError.message]
)
)
.case(type.instanceOf(ArkError), (e) =>
e.hasCode('intersection')
? e.errors.map((arkErrorError) => arkErrorError.message)
: [e.message]
)
.case("Error", (e) => [e.message])
.default((e) => [e?.toString() ?? 'Unknown error']);
return parser(error);
});
return <ErrorAlert title={title} errors={errors} />;
};
P.S. 1. I'd appreciate any guidence if there is a better solution than a match here or if i could make field.state.meta.errors in Tanstack Form more type-safe when using the useFieldContext. Probably what I should do instead is just transform the errors into strings at the form level and pass it to a pure component. 2. Thanks for this library! It's one of greatest things I've came across on my recent journey into the TypeScript land.
1 Reply
ssalbdivad
ssalbdivad2mo ago
Hey thanks for the kind words! Well for one thing I would probably define the matcher outside the scope of the component itself so it doesn't get recreated every time there is a rerender. In terms of using the string API, the best way to do that would be to use a Scope: https://arktype.io/docs/scopes Once you define a scope, you could just use the match function attached to that like:
const $ = type.scope({
ArkError: type.instanceOf(ArkError),
ArkErrors: type.instanceOf(ArkErrors)
})

const matcher = $.match({
ArkError: e => e,
ArkErrors: e => e,
default: () => ""
})
const $ = type.scope({
ArkError: type.instanceOf(ArkError),
ArkErrors: type.instanceOf(ArkErrors)
})

const matcher = $.match({
ArkError: e => e,
ArkErrors: e => e,
default: () => ""
})
That said, there are some built-in props that might help you here, namely flat on ArkError: https://github.com/arktypeio/arktype/blob/5bd1e5a08233ddd82f72841b34a8780ad2c16ba2/ark/schema/shared/errors.ts#L128 I notice there's not an equivalent prop on ArkErrors unless I'm missing something (though there are a few related like flatByPath and flatProblemsByPath, which IIRC may have been added as a convenience for a similar problem?) . If what would be most useful to you is a single property you could access on ArkErrors that would behave the same way flat does on ArkError (and I'm not missing some existing version), I'd be happy accept a PR for that so you didn't need so much custom code in the future and others could reuse your work.

Did you find this page helpful?