bcherny / json-schema-to-typescript

Compile JSON Schema to TypeScript type declarations
https://bcherny.github.io/json-schema-to-typescript-browser/
MIT License
2.95k stars 392 forks source link

Static imports for $refs #579

Closed codan84 closed 4 months ago

codan84 commented 9 months ago

Given structure like so:

schema/my-stuff.json :

{
   "id": "Stuff",
   "type": "object",
   "properties": {
      "someProp": {
         "$ref": "schema/enums/awesome-enum.json"
      }
  }
}

schema/enums/awesome.json :

{
  "id": "Awesome",
  "type": "string",
  "enum": [
    "FOO",
    "BAR"
  ],
  "tsEnumNames": [
    "FOO",
    "BAR"
  ]
}

When I run the following cli command:

npx json2ts --no-additionalProperties --no-enableConstEnums -i 'schema/**/*.json' -o packages/model/src/

I will get roughly this:

packages/model/src/awesome.ts :

export enum Awesome {
  FOO = "FOO",
  BAR = "BAR"
}

packages/model/src/my-stuff.ts :

export interface Stuff {
  someProp?: Awesome
}

export enum Awesome {
  FOO = "FOO",
  BAR = "BAR"
}

Awesome enum is now declared twice. And if I then reference this Awesome in any other schemas, it will be re-declared in every single file it is referenced.
Am I missing some way to import these instead? Or alternatively having everything in 1 file...

codan84 commented 9 months ago

To go around the issue I had to introduce a kind of root-schema to hold all assets. root.json :

{
  "id": "Root",
  "title": "Root",
  "description": "Root schema for all assets",
  "type": "object",
  "allOf": [
    {
      "$ref": "schema/new-draft.json"
    },
    {
      "$ref": "schema/draft.json"
    },
    (.......)
  ]
}

Each of the ones referenced in the above then references other schemas.
Having this I can run:

npx json2ts --no-additionalProperties --no-enableConstEnums -i 'schema/root.json' -o packages/model/src/generated-types.ts

To get something like:

/**
 * Root schema for all assets
 */
export type Root = NewDraft & Draft & (...);

/**
 * A new draft
 */
export interface NewDraft {
(...)

export interface Draft {
(...)

export interface (...)

I do not like that export of Root, but can't figure out a way to get the desired outcome any other way...

gino-m commented 9 months ago

@codan84 Are you also seeing duplicate interface declarations in the resulting output for $refs which appear in multiple places? I was trying to use --no-declareExternallyReferenced, but then individual files generate don't include import statements. :/

gino-m commented 9 months ago

As a workaround to generate unique classes, the best I could come up with is merged them into a single file. This has the benefit of not requiring a root schema, and provides unique (non-duplicate) class definitions:

mkdir -p dist/generated && \
  npx json2ts \
    --no-declareExternallyReferenced \
    --no-bannerComment \
    --cwd=src \
    -i 'src/*.schema.json' \
    -o dist/tmp/ && \
  cat dist/tmp/*.schema.d.ts > dist/generated/schema.d.ts
MattLJoslin commented 8 months ago

This is definitely a bit of a hack but one work around I found was to do json2ts --bannerComment "/ eslint-disable / import * from './base_types.d'"

Then I split the schema generation into 2 steps. One for base types which should not import themselves and another for all my other types. Of course this doesn't really solve the full issue but at least gave me a path around this temporarily.

bcherny commented 4 months ago

Dup of #258