Closed ghost closed 2 years ago
Hello @omer-to !
I understand it is confusing, but this is the expected behavior.
A constant (objectSchema
here) can only have one type. If you don't assign one to it, TS will infer it from its definition. The as const
statement forces it to be as narrow as possible. Notice the difference:
const objectSchema = {
type: "object",
properties: {
foo: { type: "string" },
bar: { type: "number" },
},
required: ["foo"],
}
type testWidened = typeof objectSchema
// => { type: string, properties: { foo: string, bar: string }, required: string[] }
// The type is not narrow, for instance, the hard string "number" is widened as a general string
const objectSchema = {
type: "object",
properties: {
foo: { type: "string" },
bar: { type: "number" },
},
required: ["foo"],
} as const
type testNarrow = typeof objectSchema
// => { type: "object" , properties: { foo: "string", bar: "number" }, required:["foo"] }
// (I omitted the readonly part for the sake of simplicity)
// The type is narrow, for instance "number" is kept as the hard string "number"
const objectSchema: JSONSchema = {
type: "object",
properties: {
foo: { type: "string" },
bar: { type: "number" },
},
required: ["foo"],
} as const;
type testAssigned = typeof objectSchema
// => JSONSchema
// The assigned type JSONSchema overrides the inferred type from TS
In order for FromSchema
to be able to infer the valid type from a schema, the input schema type needs to be as narrow as possible. Otherwise, it won't be able to apply any logic to it. For instance, if any type
keyword value is widened as a general string
or as a union of hard strings ("object" | "array" | ...
), FromSchema
won't have enough information on the schema to operate. FromSchema<JSONSchema>
will not return anything, the JSONSchema
definition is too large to infer anything.
However, that doesn't prevent it from enforcing that the input schema type should follow a certain shape, and that means extending a certain type. That's the meaning of the S extends JSONSchema
that you found in the definition. This allows S
(which should be the narrow type { type: "object" , properties: { foo: "string", bar: "number" }, required:["foo"] }
) to be validated against JSONSchema
, while not being strictly equal to JSONSchema
.
I hope it makes things a bit clearer. The main takeaway is that you cannot both assign JSONSchema
and apply FromSchema
to the same const simultaneously. Cheers !
@ThomasAribart I was struggling with a similar issue. I am trying to infer a JSONSchema argument of an execute function with this library. I was attempting it this way:
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
type Method<Input = JSONSchema> = {
input: Input;
execute: (input: FromSchema<Input>) => void;
}
const method: Method = {
input: {
type: 'object',
properties: {
name: {
type: 'string',
}
}
},
execute({ name }) {
}
}
What I'd like to achieve is that the input JSON schema gets translated as a typed argument inside the execute function. As you explained above this is likely not possible this way... Do you have any ideas to achieve the same result? Or is this not possible?
Not sure why, but by using a function to create the method I do get proper type information:
import { JSONSchema, FromSchema } from "json-schema-to-ts";
function createMethod<Input extends JSONSchema>(options: {
input: Input;
execute: (input: FromSchema<Input>) => void;
}) {}
createMethod({
input: {
type: "object",
properties: {
name: {
type: "string",
},
},
} as const,
execute: (input) => {
return input.name; // string
},
});
You can do the following:
import { FromSchema, JSONSchema } from 'json-schema-to-ts';
type Method<Input extends JSONSchema = JSONSchema> = {
input: Input;
execute: (input: FromSchema<Input>) => void; // <= Here error will not be raised because Input extends JSONSchema
}
const method: Method<typeof yourSchema> = {
...
}
The second method will work also 👍 I'm closing this issue :)
@ThomasAribart Will the satisfies
operator of Typescript 4.9 make it possible to check the type against JSONSchema
?
Yup :) Gonna add it to the docs !
Yup :) Gonna add it to the docs !
I think it should be mentioned in the FAQ
Hello,
The newly created type from a schema is
never
if the schema is typed asJSONSchema
.Let me demonstrate with an object example provided in the README.MD
When typed as JSONSchema
In the following example, I denote
objectSchema
variable asJSONSchema
, and it is indeed helpful when I am typing properties. However, when I declareObject
usingobjectSchema
, its value isnever
.Here you can see the type when I hover over
Object
:When NOT typed as JSONSchema
The very same example, however, works when I don't annotate
objectSchema
.I'm really not sure why, and I'm confused as
FromSchema
is already defined as a generic expectingJSONSchema
type, however, resulting innever
when a value typed asJSONSchema
.