Wrapping arktype with utilities
We have an implementation of an in-house ORM layer that is built upon arktype. It's been really amazing to work with and supports some of our huge model types that have hundreds of fields while remaining snappy.
We have a set of prebuilt core types that these models have to use in order for the downstream system to know how to persist and generate APIs around them appropriately. Unfortunately, I haven't been able to figure out how these could work with the core AT constructs because they encompass typing + defaulting behavior which we want standardized + metadata that the downstream systems use
we define our core model types like this
and then use them in model definitions like this
1. Is there a better way of setting this kind of thing up with scopes/modules or similar?
2. This works fine for these simple primitives, but I can't figure out the typing for enums, objects, or arrays. I'd like a way to take in the input to AT's type function and pass it through and still get all the nice type inference back so you could do things like
-
ModelTypes.enum(['yes','no']) and get back the equivalent of type('yes | 'no' | null).configure({ typeAlias: 'enum' }).brand('enum').default(null)
- ModelTypes.object({ subfield: ModelTypes.string() }) to get back type({ subfield: type.string.default('') }).or(type.null).default(null)
Is there a nice way to define a function like that?5 Replies
Awesome to hear about the success you've had with arktype!
The first snippet you mention does look suspiciously like a scope. The only difference I can see is the parameterized metadata, but since it's optional I wonder if it would be functionally equivalent to just call
.configure directly as needed?
There's nothing wrong with your current approach, though. Maybe not parameterizing the metadata could be better depending on the context so it's just a simple object with Type instances as values- that is essentially what a module is. The big advantage of a scope over something like this is the ability to string-embed the keywords you create directly into your expressions in addition to being able to use them independently as a module.
That said, yes, the patterns (I think) you're describing are quite easy to wrap externally and it's great for integrating arktype validation+inference with your API:
https://arktype.io/docs/generics#external
the really cool thing is with the second approach, you don't even need to pass a type to your API- you can just pass a native definition and parse it in your function.
worth noting also that .configure by default is additive so you could apply some base metadata internally and then external .configure calls chained off that type would have both parts of the metadata. the API itself is actually pretty flexible as well, e.g. you can pass a function that accepts the current metadata attached to the type and returns the updated metadataThanks! I'll give the external generics a shot
Good luck! There's a lot under the
type namespace that would be useful here. Let me know if you get stuck looking for a particular utility I can probably help out if there's a clearly defined API you're aiming atOk I think I figured this out, sharing the code here in case it helps others in the future. @ArkDavid let me know if this is totally wrong or something. Idea again is these helper types which bake in defaulting and metadata assumptions.
Nice, this all looks reasonable to me! There may be some cleaner ways to handle casting in the implementations by using overloads and/or
type.raw to accept arbitrary definitions but in the end it is mostly inconsequential since it won't affect external consumption. Great work 🙌