Zod "parity"? (nanoid, emoji,...)

Hello! ArkType is awesome! Been really enjoying replacing Zod in my project with it to try it out. 😁 I won't assume this is the case but are there any plans to implement some of the nice little extra "features" Zod has such as its nanoid and/or emoji validations?
10 Replies
Maxzilla
MaxzillaOP2mo ago
I know I can just write custom narrow functions for now.
ssalbdivad
ssalbdivad2mo ago
Definitely do plan to add more string.x keywords over time, but not necessarily specifically because they exist in Zod (although if a lot of people are relying on them that makes it more likely). Also not sure if you know how scopes work, but it makes it quite easy if you do define a custom narrow to define a new keyword centrally you can use anywhere as if it were a built-in. In addition to being useful for lots of people, the biggest factor I'd use to evaluate adding a keyword is how well-defined the standard is. For something like string.date.iso where I can include a regex defined by the standard itself, there's very little cost to adding the keyword. However, for e.g. string.email where there is no authoritative definition, it's a lot more daunting to both arrive at a solution I am confident in and not create bad intuitions for some developers. In that case it is so broadly used that it outweighs the downside, but you can see why we don't have a keyword for e.g. string.phoneNumber yet. Quite widely used, but so many different formats you might expect that committing to a default could be a problem.
Bobakanoosh
Bobakanoosh2mo ago
@ArkDavid so is your recommendation to have one big global scope that all schemas use, which defines custom types, as opposed to creating types and reusing those? Ex:
const {type } = scope({
"string.emoji": arkType("string").narrow((str) => /* Placeholder */ true)
})

const stringEmoji = arkType("string").narrow((str) => /* Placeholder */ true)

const someSchema = type({
input: stringEmoji,
output: "string.emoji",
})
const {type } = scope({
"string.emoji": arkType("string").narrow((str) => /* Placeholder */ true)
})

const stringEmoji = arkType("string").narrow((str) => /* Placeholder */ true)

const someSchema = type({
input: stringEmoji,
output: "string.emoji",
})
ssalbdivad
ssalbdivad2mo ago
That is largely a personal preference. Scopes make it possible to express cyclic types and also open up your custom types for the same TS syntax you can use with builtins. That said, the goal is to be able to do as much as possible just by composing types without having to know scopes exist, so you should do what feels better to you/for your use case. I just like to mention it when people ask for specific keywords in case they don't know there's a way to get something equivalent to a built-in keyword externally
Bobakanoosh
Bobakanoosh2mo ago
Awesome, I find some libraries are opinionated and it’s easier to follow those opinions rather than fight them, so was just checking.
ssalbdivad
ssalbdivad2mo ago
You can think of Scopes as a power-feature (same with anything marked advanced in the docs). You will never need them unless you have co-recursive types, but they might offer more benefits the more you are maintaining in ArkType
Maxzilla
MaxzillaOP2mo ago
Incredible. I indeed hadn't looked at scopes yet. Thanks for the answer!
Bobakanoosh
Bobakanoosh2mo ago
So there’s no pitfall/repercussions of using a global scope for that purpose? Just wanting to make sure if I go that route I’m not gonna be 3 months down the road and realize some validation isn’t gonna work because I’m using that global scope. Maybe a paranoid thought
ssalbdivad
ssalbdivad2mo ago
No definitely not. The global scope is really just another scope. There's nothing special I do internally you can't do externally And in fact by using a single scope, you're improving caching at runtime and a type-level You could make that single scope your own, but if you made lots of small scopes it would be less efficient
Bobakanoosh
Bobakanoosh2mo ago
Beautiful, thanks!

Did you find this page helpful?