Skip to content

Latest commit

 

History

History
60 lines (48 loc) · 2.21 KB

does-json-schema-to-ts-work-on-json-file-schemas.md

File metadata and controls

60 lines (48 loc) · 2.21 KB

Does json-schema-to-ts work on .json file schemas?

Sadly, no 😭

FromSchema is based on type computations. By design, it only works on "good enough" material, i.e. narrow types ({ type: "string" }) and NOT widened ones ({ type: string } which can also represent { type: "number" }). However, JSON imports are widened by default. This is native TS behavior, there's no changing that.

If you really want use .json files, you can start by upvoting this feature request to implement .json imports as const on the official repo 🙂 AND you can always cast imported schemas as their narrow types:

// dog.json
{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": { "type": "integer" },
    "hobbies": { "type": "array", "items": { "type": "string" } },
    "favoriteFood": { "enum": ["pizza", "taco", "fries"] }
  },
  "required": ["name", "age"]
}
import { FromSchema } from "json-schema-to-ts";

import dogRawSchema from "./dog.json";

const dogSchema = dogRawSchema as {
  type: "object";
  properties: {
    name: { type: "string" };
    age: { type: "integer" };
    hobbies: { type: "array"; items: { type: "string" } };
    favoriteFood: { enum: ["pizza", "taco", "fries"] };
  };
  required: ["name", "age"];
};

type Dog = FromSchema<typeof dogSchema>;
// => Will work 🙌

It is technically code duplication, BUT TS will throw an errow if the narrow and widened types don't sufficiently overlap, which allows for partial type safety (roughly, everything but the object "leafs"). In particular, this will work well on object properties names, as object keys are not widened by default.

import { FromSchema } from "json-schema-to-ts";

import dogRawSchema from "./dog.json";

const dogSchema = dogoRawSchema as {
  type: "object";
  properties: {
    name: { type: "number" }; // "number" instead of "string" will go undetected...
    years: { type: "integer" }; // ...but "years" instead of "age" will not 🙌
    hobbies: { type: "array"; items: { type: "string" } };
    favoriteFood: { const: "pizza" }; // ..."const" instead of "enum" as well 🙌
  };
  required: ["name", "age"];
};