Compile-time typesafe merge/and for data-only "traits"
I've evaluating introducing arktype to a typescript codebase, but I got stuck trying to use it to set up my POJO "traits" system. This is similar in concept to the Trait abstract class in
arktype/util
but this is data only; so far I've avoided the need to introduce any classes and I'm hoping to maintain this approach.
So I have a discriminated union that represents the different "traits" and the union members share a common shape (mainly references to other trait names), something along these lines:
I'm confused about how I would represent this setup in arktype in a typesafe manner? Or would you suggest an alternative way of representing this? I don't have any significant code written against these types so I'm open to rethinking this setup entirely.
I got stuck almost immediately as neither .and(...)
nor.merge(...)
will allow me to inherit compile-time type checking of requires
in my Trait
subtypes.
Please help! Thanks24 Replies
What specifically do you want to work that doesn't?
I guess you want a way to inersect two objects so that the traits arrays can be intersected as sets?
There's an API for writing arbitrary type-level logic as a generic:
oh
If you're wanting to map over two objects and intersect them except the
traits
key which you want to treat as a set instead of an array, I guess I'd say merge
them then go back into the type and replace the requirements
key by manually concatenating the lists
The easiest change to the API you could make that would make this trivial was just having your trait sets be an object instead of a tuple e.g. {HasMouth: true, TraitCanScream: true}
then you can intersect them without worrying about itMaybe that's the solution, I had in my head that I would be able to get what I wanted without the ark equivalent of the
TraitDefn
generic above, but it's likely that ark needs it to be generic for the exact same reason TS needs it i.e. TS won't type-check the requires
with &
either:
Yeah, best to use an object to simplify things tuples are treated positionally so intersecting them will make the type unsatisfiable if you have multiple literals
If you do that you will just be able to intersect them normally and the inference will be fine
@ArkDavid so how would you represent
type TraitDefn<T extends Trait> = Trait & T
in ark?That doesn't do anything since
T
extends Trait
intersecting should be a no-opI'm looking at
If you want a generic with constraints you can do
generic(["T", yourTraitType])("T")
it's what's giving me type-checking in
I meant the
Trait & T
part it should just be T
ah
yes
you're right
@ArkDavid ok that generic thing got me further! but now I'm having trouble when the requires isn't an empty array:
Name should be
"'TraitCanScream' | 'TraitHasMouth'"
like TS or you can use type.enumerated()
and pass them thereI know there's a lot missing from the docs but do check those out and see if some of what you're looking for is there https://arktype.io/reference/api/
This worked!
I don't understand the difference though
It's just like TS if you put them in a tuple like that it's a tuple type
ahhhh
You can mouse over it and see how it's inferred to see why it doesn't work
right, it's just like ts
I had zod's
z.union([type1, type2])
syntax in my head
right of course
I didn't even look at that because library types are usually super nestyIt will help to just get in the habit of mousing over your type to see how it was inferred if something is not working the way you expect
The hovers (unlike Zod) are usually very self descriptive 😛
👍 thanks for your help
No problem good luck!
a quick-fix could be to lift a bunch of examples out of
__tests__
onto the siteInteresting idea I will consider that
I mean honestly even though I'm working on better docs it will take time to get every feature covered whereas every feature is already thoroughly tested. Guess it depends how hard the tests are too read for someone trying to learn the framework
You have to work through extra stuff visually like the attest calls