Using scope with morphs
I store some data in a json file, so I'm using
string.json.parse
to read that data in and validate it.
Maybe I shouldn't be using scope here and should instead be directly using the windowMeta
type within the Record
?31 Replies
Yeah this is a common gotcha that I will add to docs.
type
is the type function from the default scope so it doesn't know about your keywords
Normally, there are two options for stuff like this so you can reference your aliases.
One would be a tuple expression, e.g. instead of doing something like type({bar: "number"}).and({foo: "string"})
you can use [{bar: "number"}, "&", {foo: "string"}]
.
The tuple expression for a morph is "=>"
, but I'd actually never considered that to
is kind of unique since it is kind of sugar for a morph allowing you to just specify the output. Maybe worth creating a tuple expression syntax for that.
All that to say what the actual solution iss..
Which is a thunk expression, which allows you to invoke type
from the current scope. One sec I'll link some examplesExplanations are helpful so you're all good lol
GitHub
arktype/ark/type/tests/thunk.test.ts at a64f083ca2da8009fbc8de8...
TypeScript's 1:1 validator, optimized from editor to runtime - arktypeio/arktype
It's actually crazy this even works in TS haha
So basically
ahhh self referencing kinda
very nice ok
This does break the
type.Any<TData>
from the other example though ๐
which I kinda get since it's not taking an object any moreHmm, I mean it's probably not this specifically that broke it but just the types resolving correctly
GitHub
Tuple expression for
.to(def)
ยท Issue #1205 ยท arktypeio/arktypeThere is an existing tuple expression for morphs (using "=>"), but it accepts a function as the second param. .to is a helpful sugar that allows providing a validated definition, but t...
Oh I see
Because youre previous version was just an object
Right yeah but now it takes a string and outputs an object
Btw important that modules are not schemas they are groups of schemas but you can't use them to directly validate anything
thats what
.export()
is for right?KEKW actually what I said wasn't even right
so you can use the schema within the group
scope allows you to define things
you can export it to a module which is a collection of schemas
but neither one is itself a schema
types.config would be a schema where types is a module
Yeah I end up passing
$.export().config
to the class's super
Hmm this is interesting TS is complaining here though. I mean assignability can be a little weird with parse encoding stuff but ideally everything shoudl be assignable to
type.Any
so maybe this is a bug?Here's the full snippet:
error is:
Yeah I see
This seems like maybe just TS being weird?
This is allowed:
Seems like it can figure it out if you infer the input from the whole type instead of just what it
.infers to:
```ts
import { scope, type type } from "arktype"
class SomeClass<t extends type.Any> {
constructor(public schema: t) {}
}
const $ = scope({
config: () =>
$.type("string.json.parse").to({
windowMeta: "Record<string, windowMeta>"
}),
windowMeta: {
"position?": {
x: "number",
y: "number"
}
}
})
const types = $.export()
const t: type.Any = types.config
type UserConfig = typeof types.config.infer
class SomeParentClass extends SomeClass<typeof types.config> {
constructor() {
super(types.config)
}
}
````
Ohhhh
I 100% get it now
It actually makes sense what happened originally I forgot you were explicitly passing
UserConfig so it wasn't being compared to
any
So the reason you got an error when you turned it into a morph is that
.infer extracts the *output* type, whereas
types.config is going to be a type that includes the whole morph.
So depends a bit on how you want to pass those types around and which you need where, but the easiest thing would be to just change:
```ts
type UserConfig = typeof types.config.infer
```
to
```ts
type UserConfig = typeof types.config.t
```
t is the whole type representation with inputs and outputs. You shouldn't use it directly as a value- for that you'd want
infer or
inferIn`. but to pass whole types around that's the way to do ithmmmm okay trying that 1 sec there's a few other things I haven't included in my snippets thus far lol
I mean either solution would work really, but I'm glad to see it actually makes sense there's an error there. When generics and TS are involved sometimes I just assume it's nonsense haha
I guess I should try and focus on actually getting docs done so you and other people can read about this stuff lol
I may be trying to avoid that by answering other questions ๐
Admittedly would be nice, I know you're super busy though ๐
even if they're not docs but moreso "recipes". Common use-cases that cover mechanics of the library that aren't super evident/easy to think of at first. e.g. type for
password
and confirmPassword
. I figured that out eventually but a recipe of having one field rely on another of it's own type would've helped a ton.Yeah that is a common one
You should have searched the Discord though ๐
I keep wanting to use this lib because I see the potential but it can be demotivating losing hours to it :/ (as it usually goes with complex types though)
I might've searched the discord tbh i forget
was weeks ago
There really has been a lot of progress recently I'm getting ready to publish. To be fair some of these are not written yet but we're getting there ๐ง
It's definetly come leaps and bounds from even a few weeks ago, I see the progress ๐
But the other part of the problem I think is that a lot of people are trying to integrate ArkType with their own complex stuff and then you run into problems that are both related to quirks of TS as well as having to deal with internal parts of arktype that most users are not really meant to deal with
And TBH that stuff will also happen if you're e.g. writing a Zod schema and wanting to pass it to your own generics based on constraints
And TBH that stuff will also happen if you're e.g. writing a Zod schema and wanting to pass it to your own generics based on constraintsYeah I ran into this when making some frontend form/form input components
And also TBH the docs won't help that much there either because they're really meant for the more core use cases, and I can't really cover all the nuances of generic inference when integrating with external TS
But stuff like a
type
call not being resolvable within a scope is something lots of people have and will run into so I hope I can at least cover some of those bases
And maybe add some FAQs about integrating external generics since I get so many questions about that especially here hahaYeah those are some good "basics" that I think would go a long way. Appreciate all your work!!
For posterity, finally got everything working, last part was having a default value in the event the parsing failed. For that I just needed to use the type
type.Any<TData>['inferOut']
Thanks again!Yeah if you need to extract the output that's what you want or
['infer']
is actually an alias of that