Can the JSON Schema be typed automatically?
I'm trying to use the generated JSON Schema inside forms in order to provide useful information to users, such as if the field is required/optional, if there is a max length limit, etc..
I'm using this schema:
The JSON Schema I get, is a
JsonSchema.NonBooleanBranch
which is too broad and I can't really use it in TS without a lot of defensive checks and casting.
Ideally I'd like to use it like this:
30 Replies
This works, but with TS errors such as :
You can't really get more narrow types by default. You just need to narrow the type down by checking property access
E.g.
"type" in schema && schema.type === "number" // narrows to a JSON Schema for a number
or w/eThat's a bummer. Is there any know libs that do the heavy lifting for us?
Why can't you just narrow?
It's should be simple enough π€·
And if you need to re-use it then create a custom type guard/predicate: https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates
Documentation - Narrowing
Understand how TypeScript uses JavaScript knowledge to reduce the amount of type syntax in your projects.
It feels kind of counterproductive. My goal is to reuse the schema to get the values like required/maxLength.
If I check property access it feels like a rabbit hole.
My first tests were unconclusive.
Here's a sample:
In this instance,
type
does not exist in schema.properties[field.name]
Property 'type' does not exist on type 'NonBooleanBranch'. Property 'type' does not exist on type 'Const'.ts(2339)here's my schema:
Why exactly are you doing these operations on the JSON Schema and not the ArkType type itself?
Not sure how I could use the type itself π€
I don't want to validate, I want to get the validation rules if that matters
I'm not super familiar with this part of the api, but iirc there's stuff like
type({foo: "string>3"}).get("foo")
Indeed, is there a way to get the "maxLength" from that?
I guess try it and see
You can also traverse the
typeInstance.json
which I know definitely has the maxLength (possibly under a different key name) but I'm not sure how stable that's considered -- I think it's considered stable enough though, as of 2.0
Yeah, .get
just returns the equivalent typeInstance
for that key
So I think the solution is to use typeInstance.json
/typeInstance.get("key").json
@SimonAh yes, that looks promising
Having implemented the source code for parsing JSON Schema into ArkType schema I'm pretty familiar with ArkType's internal schema system (returned by
.json
), so if you have any specific queries on it feel free to ping me (afaik it's not documented anywhere)Looks like it returns either an object or an array of objects
depending on the number of rules I suppose or the kind of rules
Iirc an array represents a union
Which seems to match what you've got there
I.e. the "summary" property is either "a string with maxLength 4000", or
undefined
. Which I imagine you can confirm by looking at the .description
instead of the .json
( @Simon )Fwiw you can get an idea of what some of the different ArkType schemas look like by browing the
ark/jsonschema/__tests__
on this commit (at least for string/number/array/object): https://github.com/TizzySaurus/arktype/blob/f6268f7727bf0c51b133d3a24b08cc1bf025094f/ark/jsonschema/__tests__/string.test.tsGitHub
arktype/ark/jsonschema/tests/string.test.ts at f6268f7727bf0c51...
TypeScript's 1:1 validator, optimized from editor to runtime - TizzySaurus/arktype
Got to something with some checks.
I suppose I can make helpers and use those as I won't have that many different cases
Fwiw
(Object.keys(property).includes("maxLength")
can be simplified to "maxLength in property
I'd also be inclined to merge the return max
if statements since they all do the same thing
And since it's repeated, maybe it's worth moving it to a util function
Although it's not really clear what those if statements actually addbummer that I can't seem to narrow to the JsonObject instead of dismissing string, boolean, number etc..
As I said originally, just do
"type" in jsonSchema && jsonSchema.type === "object"
(in theory, anyway)Ah yeah, also need to check if not null
Thanks for guiding me through this π I think I have what I need to go further now
@Simon We're about to release docs for the
select
method which will really help with the kind of introspection you're describing directly in ArkType. Here's a snippet from those (the API is already available in the current version):
select
is the top-level first method we're introducing for interacting with a Type based on its internal representation.
It can be used to filter a Type's references:
When you have a constraint node like MinLength, you can access .rule
to get its value
(plus all sorts of other useful properties/methods)Very interesting. Thatβd make it really more easy to deal with!
Just tested and that's awesome.
I could quickly create a few helpers that are easily typed & reused
Looks great! You'd only have multiple shallow max length fields in the case of a union, so in that case you'd want to return the least strict one?
Makes sens yeah
@ArkDavid is the
.select
function costly?
I wonder if I can run a few of those for each field and automatically add the components (like "β optional" or "0 / 4000 characters") if the constraints exists or if there will be a heavy price for large forms
Looks like my isOptional is not stable for all schemas.
Is something like this supposed to be the way?
It is cheap it is just iterating over an existing list of references and filtering them
if you're looking for optional properties you should just filter by
kind: "optional"
I'm having some weird results with the select but maybe I'm misunderstanding how it works.
Given the following schema:
Prints:
Which is expected.
This however returns an empty array.
Optionality belongs to the key, not the value. Once you get the value, you don't have the optional node anymore
You want to select optional nodes then you can map those to
c => c.value
if you want the associated valueOh I missed that, makes sense