0

If I have a JSON schema saved in a file like f1040.json with content:

[
  {
    name: "L1",
    type: "String"
  },
  {
    name: "C2",
    type: "Boolean"
  },
  {
    name: "L3",
    type: "String"
  },
  ...
]

And I want to generate a type that looks like:

type F1040 = {
  L1: string;
  C2: boolean;
  L3: string;
  ...
}

How can I generate this without specifying each field manually (there are hundreds of them)? My first attempt at a solution isn't valid typescript (bc I'm providing more then one mapped type I think) but hopefully this invalid example clarifies what I'm trying to do:

import { f1040 } from "./f1040.json";

const bools = f1040.filter(e => e.type === "Boolean").map(e => name) as string[];
const strings = f1040.filter(e => e.type === "String").map(e => e.name) as string[];

export type F1040 = {
  [key in (typeof bools)[number]]?: boolean;
  [key in (typeof strings)[number]]?: string;
};

My incorrect solution was inspired by an answer to a similar but different question: TS create keys of new type from array of strings

Edit1: The solution doesn't need to be dynamic but if it's a build-time solution then it needs to play nice with rollup & the declarations bundler I'm using

Edit2: Another unsuccessful attempt, this time utilizing @sinclair/typebox:

import { Static, Type } from "@sinclair/typebox";

import { f1040 } from "./f1040.json";

const F1040 = Type.Object({
  ...f1040
    .filter(e => e.type === "Boolean")
    .map(e => e.name)
    .reduce((res, f) => ({ ...res, [f]: Type.Boolean() }), {}),
  ...f1040
    .filter(e => e.type === "String")
    .map(e => e.name)
    .reduce((res, f) => ({ ...res, [f]: Type.String() }), {}),
});
export type F1040 = Static<typeof F1040>;

const f1040Data = {
  L1: true,
  C2: "Yea",
} as F1040

The above attempt builds fine w/out any error.. which is too bad because the type assignments at the end are wrong. This should fail to build with a TypeError saying something like

Types of property 'L1' are incompatible. Type 'boolean' is not comparable to type 'string'.

bohendo
  • 421
  • 1
  • 5
  • 7
  • Do you have to do this dynamically? It would be trivial to write something that would take your JSON schema and write an `interface` or `class` definition in a `.d.ts` file... In fact I'm sure someone has already done it. – Heretic Monkey Oct 27 '21 at 20:41

1 Answers1

1

It cannot be done dynamically, because your typescipt program is compiled into javascript before it is run, and in javascript all type information is removed. Types are only used in the typescript compilation process.

So you need to have the types before typescript compilation. E.g. using https://www.npmjs.com/package/json-schema-to-typescript

mrtnlrsn
  • 1,105
  • 11
  • 19
  • If I compile `f1040.d.ts` from a schema derived from `f1040.json` then how do I import the newly compiled type into an `index.ts` file in the same directory? The answer to [this Q](https://stackoverflow.com/questions/61308006/how-to-correctly-import-custom-types-in-typescript) suggests that we need a `file.ts` alongside each `file.d.ts` but I'd rather not add a bunch of `f1040.ts` files for each form I'm using. – bohendo Oct 27 '21 at 22:42
  • 1
    @bohendo No need for `file.ts`. You can do `import { YourType] from './f1040'`. Module resolution will add `.d.ts`. https://www.typescriptlang.org/docs/handbook/module-resolution.html – mrtnlrsn Oct 28 '21 at 19:04