json-schema-to-typescript icon indicating copy to clipboard operation
json-schema-to-typescript copied to clipboard

Adding a description to a $ref object referencing an enum inlines the enum

Open JoshuaKGoldberg opened this issue 3 years ago • 3 comments

Perhaps related to #470, but not the same issue. Here, I'm seeing that when a $ref pointing to a defined enum has a description, the enum gets inlined.

{
  "$definitions": {
    "shared": {
      "enum": ["a", "b"]
    }
  },
  "properties": {
    "first": {
      "$ref": "#/$definitions/shared",
      "description": "A first property."
    }
  },
  "additionalProperties": false,
  "title": "Example Schema",
  "type": "object"
}

Note that if the description is removed from properties.first, the export type Shared = "a" | "b"; gets printed as expected.

Actual

export interface ExampleSchema {
  /**
   * A first property.
   */
  first?: "a" | "b";
}

Expected

export type Shared = "a" | "b";

export interface ExampleSchema {
  /**
   * A first property.
   */
  first?: Shared;
}

JoshuaKGoldberg avatar Jul 29 '22 07:07 JoshuaKGoldberg

Referencing other PRs that could be related to this, at least from the description perspective:

https://github.com/bcherny/json-schema-to-typescript/issues/193 https://github.com/bcherny/json-schema-to-typescript/issues/466

mribichich avatar Jan 24 '23 14:01 mribichich

Doing some deeper investigation into this - the reason that this happens is due to how the json-schema-ref-parser lib works.

json-schema-to-typescript uses json-schema-ref-parser (specifically boris's fork) and the library essentially scans the schema looking for $ref's and it replaces the $ref with the referenced schema.

It does this by merging the referenced schema into the current schema. I.e. newSchema.properties.first === { ...oldSchema.$defs.shared, ...oldSchema.properties.first } However - if the library sees { $ref: '#/path/to/ref' } it has a short-cirtuit path which lets it return the referenced schema directly. Above logic is here: https://github.com/bcherny/json-schema-ref-parser/blob/3241ca426d7364455ca8d85f231d51211d7ab7fe/lib/ref.js#L271-L292

When the parser attempts to look to see if the referenced schema has a definition (so that it can get the key and thus the name), it just does an === check to see if the current schema is a definition schema: https://github.com/bcherny/json-schema-to-typescript/blob/6e3fbca000b51b68a89c9d14d0563dfae8f8a989/src/parser.ts#L123

So obviously if you have a simple just $ref object, this check will find the key, but as soon as you add any other prop, the check fails to find the key because it's now a new object.

@JoshuaKGoldberg's fix (#473) works around this specifically for enum types by doing a lookup to see if the .enum properties are equal. It's a hack that works because (as above) it's a shallow spread copy.

What's the correct, general solution for this? Well we probably need to keep a property which tracks the ref path for a given schema so that we can resolve it instead of looking up the key.

bradzacher avatar Apr 12 '23 09:04 bradzacher

I tried solving this (#521) but I ran into a problem in that the description comment gets put in the wrong spot (on the referenced schema, not on the referencing thing).

The codebase is pretty complex so I'm not sure how to work through this more right now, but at least I've prototyped a generic solution for this.

bradzacher avatar Apr 12 '23 09:04 bradzacher