Wrapper around `type()`
In my configuration loader that I'm building, I have a "registry" pattern where I want to have developers declare the type schema of the config they expect, and then they need to register the config in order to get it loaded, looks something like this:
But I don't love having
registerConfig()
wrapped around type()
, instead I would love to just have the second parameter to the register function be passed through from type()
so that I could call type()
internally, i.e.
This is ideal as it avoids the need to import both my config library and arktype, as well as just being simpler for the consumer of my library.
The problem I'm having is that Parameters<typeof type>[0]
is not resolving correctly. Is there a better way to get to that type?51 Replies
This is the pattern you'll want to use:
Oh huzzah! What's the name of the
<const def>
syntax? I see that all over the arktype codebase but I've not used it previouslyI've just heard const type parameters, it was introduced in TS 5.0 I believe
ahhh
I'm getting some angry
ban-types
linting on the use of {}
here, is that expected?
And type(def)
is not happy either
the ts error for type(def)
is
it goes on for many lines after thatA lot of built-in lint rules suck
Well at least for working with complex types like this
got it
mostly making sure that use of
{}
isn't some weird footgun that I need to be worried aboutYou will want to cast
def
to never
there, since it's trying to validate it.gotcha
No it should be used in a scenario like this as a "baseline" for a mapped type
I think in a very recent version of eslint they actually changed the default complaint about that type to hopefully be more descriptive
Personally I think the lint rule about
{}
is actually more of a foot-gun than {}
itself
I've seen a bunch of people struggling with types where they followed the advice of the lint rule and replaced it with Record<string, never>
or similar then tried to map over that but it adds an index signature you definitely don't want when used in that wayYeah I've definitely hit that before
I built a pretty cool ts rpc library called
cheep
that had some great complex typing stuff, this is bringing back memories for meI think TS's greatest strength is the flexibility it provides library authors to create amazing DX at the cost of a lot of casting internally but the trade-off is worth it because if written correctly, end users get more precise types and have to cast less
But a lot of people get caught in the mentality they should never have to cast which if you're writing anything to do with runtime validation you have to give up on very quickly 😛
aint that the truth 🤣
Thanks for all the help, between your quick responses and the awesome work you've done on
arktype
this little project has been remarkably smoothGreat to hear! Hopefully things will be even easier soon once the new docs are done 🙏
Looking forward to it!
Hey @ArkDavid - Super excited to see v2 landing, I'm in the process of upgrading and am a bit stuck on upgrading this pattern with the changes that happened under the hood; I've tried converting to this, but I'm not clear on what the second arg to the new
*Root
types should be:
(I'm getting validateRoot
and inferRoot
from @arktype/schema
)
I took a shot in the dark and used {}
but that yields a bunch of errors on previously good tests, to the tune of Type '{ base_url: string; }' is not assignable to type '"'server' is not on an intersection schema "'.ts(2322)
(example usage with that error:)
I think you can just use
type.infer<def>
And type.validate<def>
Oh excellent
Thanks!
Let me know if you have any issues!
One more question: I had previously been accessing
.description
on the result of calling type(def)
in some logs, is there something similar to that elsewhere?
Oh wait, that should still be there, I think I just have a bad cast happeningYeah should still work! Had me worried for a second haha
Hmmm, actually I'm a little stumped:
config.description
is not happy thereThis is likely just a side effect of how TS assigns wonky types to values derived from generic parameters within an implementation
Ok, so safe to ignore then? I can cast for this specific use
One option would be an internal and external signature:
type.raw
may also work
type.raw
is probably the easiest solution I guess
It's just type
without any inferenceahh
I don't think you need
distillOut
eitherYeah was wondering about that; should I be able to do all of this without touching
@arktype/schema
?Let me double check
You can use
type.infer.Out<def>
You should, and everything exposed through arktype
directly will be stable whereas @ark/schema
is still notawesome; looks like this works for me, do I have it right?
I don't think you should need to cast?
Oh, so I should use
type.raw
instead of type()
Yeah
They're the same thing under the hood but it avoids type-level validation/inference that is meant for literal definitions
Ok, that combined with the internal/external definition seems like a good strategy
Thanks!
I don't think you should need the internal/external definition unless you still want it
I guess you'd have to cast the return though if you get rid of it so whatever you prefer
Yeah, casting the return is probably fine
Either way- glad it worked out 😊
Thanks again for the great work on this library, I'm finally getting some of my coworkers turned onto it after months, which is fun to see
Exciting the docs are finally in a place people can get up and running! Though admittedly still harder for people writing wrapper APIs like you, still need to add more docs for those types
And honestly no matter how much I do document there will always be nuances of TS that can make that tricky
Yeah totally; I appreciate the immense effort that clearly went into the docs up to this point
Ok, odd question here, this should work, right?
I'm getting
Type '"string.url"' is not assignable to type '"'string' must reference a module to be accessed using dot syntax "'
this is using pure arktype, not my wrapperThis is the kind of thing that can occur if certain imports are not being resolved correctly
Do you have moldule/moduleResolution
NodeNext
or similar in your tsconfig?
Ideally it should also work for older settings but that's my guess as to what's going onmodule resolution is
node
this is in an Nx monorepo, so tsconfigs are a bit of a wormholeYeah I would highgly recommend updating that as the features
node
is missing have been in node since like v12Yeah, that's very valid
thanks for the pointer
Basically it means you can't resolve any package.json exports
and that fixed it
Which is likely to cause issues for lots of other libraries as well
I suspect that may have also caused some issues with remix and shacdn now that I think of it
But also thanks for the heads up, there's probably something I can do on my end to make sure it works for node as well.... I noticed some issues with the tests that would have caught that issue in the last release
Yeah it's really unfortunate,
node
should really be deprecated at this point
It sounds innocuous but it's so outdated nowHmmm, so I thought I had this sorted, but came back to it today and found that just changing to
nodenext
didn't solve it for me; here's the fully resolved tsconfig for reference, hoping that maybe you can spot something else that isn't playing nicely 🤞
scratch that seems to have been a weird state that got trapped in the TS language server; went away after a restartI would also upgrade to
2.0.2
I think I removed the import that would have broken "node"
, although TBH I couldn't even get node working anymore internally for the tests so it feels like that ship has sailed in terms of support haha
I noticed it is officially deprecated by TS at least 🎉Yeah, I get a tooltip telling me that its a bad idea at the very least!
Nice, not sure when that happened but glad I'm fighting slightly less of an uphill battle now haha
The features not supported in "node" (package.json
exports
) were added in Node 12 so it's crazy out of date