Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Intersection schema generation is order-dependent #603

Merged
merged 5 commits into from
Jul 22, 2024

Commits on Jun 24, 2024

  1. Add new test cases.

    altearius committed Jun 24, 2024
    Configuration menu
    Copy the full SHA
    fb9fc26 View commit details
    Browse the repository at this point in the history
  2. Intersection schema generation is order-dependent

    - Given a schema that contains a named definition (`B`),
    - And that named definition is referenced in multiple locations,
    - And that named schema is also an intersection type (`allOf` in this
      example),
    
    Then when parsed, the generated TypeScript will contain the correct
    reference only for the _first_ location in which the named schema is
    encountered, during a depth-first traversal.
    
    Subsequent references to the same schema will be generated as though
    they were only the intersection type, and not the named schema.
    
    Example
    
    Given the following schema:
    
    ```yaml
    $id: Intersection
    type: object
    oneOf:
      - $ref: '#/definitions/A'
      - $ref: '#/definitions/B'
    
    definitions:
      A:
        type: object
        additionalProperties: false
        allOf: [$ref: '#/definitions/Base']
        properties:
          b: {$ref: '#/definitions/B'}
      B:
        type: object
        additionalProperties: false,
        allOf: [$ref: '#/definitions/Base']
        properties:
          x: {type: string}
      Base:
        type: object
        additionalProperties: false,
        properties:
          y: {type: string}
    ```
    
    The current resulting TypeScript will be (comments adjusted
    for clarity):
    
    ```ts
    // Incorrect: should be `type Intersection = A | B`
    // Note that the B type at this location is the _second_ reference to
    // B during a depth-first traversal.
    export type Intersection = A | B1;
    export type A = A1 & {
      b?: B;
    };
    export type A1 = Base;
    export type B = B1 & {
      x?: string;
    };
    export type B1 = Base;
    
    export interface Base {
      y?: string;
    }
    ```
    
    Root Cause
    
    In `parser.ts`, [lines 57 - 75][1], when schema that matches multiple
    "types" is encountered, the parser generates a new `ALL_OF` intersection
    schema to contain each sub-type, then adds each sub-type to the new
    `ALL_OF` schema.
    
    Each sub-type is then parsed sequentially. During this process,
    `maybeStripNameHints` is called, which mutates the schema by removing
    the `$id`, `description`, and `name` properties.
    
    Notably, these properties are used by `typesOfSchema` to detect the
    `NAMED_SCHEMA` type. As a result, this schema object will never again be
    detected as a `NAMED_SCHEMA` type.
    
    Therefore, the _first_ instance of the schema object is correctly
    handled as an intersection schema **and** a named schema, but all
    subsequent instances are treated as though they are **only** an
    intersection schema.
    
    Proposed Solution
    
    - The call to `typesOfSchema` is moved from `parser.ts` to
      `normalizer.ts`, with the goal of avoiding confusion due to a mutated
      schema object. The resulting list of schema types is persisted on the
      schema using a newly-introduced `Types` symbol.
    
    - The generated intersection schema is _also_ moved from `parser.ts` to
      `normalizer.ts`. This is because it is advantageous to let the
      generated intersection schema participate in the caching mechanism
      (which it could not previously do, since it was generated dynamically
      during each encounter). Without this, multiple instances of the same
      schema are generated.
    
    Related Issues
    
    - bcherny#597
    
    [1]: https://github.com/bcherny/json-schema-to-typescript/blob/31993def993b610ba238d3024260129e31ddc371/src/parser.ts#L57-L75 'parser.ts, lines 57 - 75'
    altearius committed Jun 24, 2024
    Configuration menu
    Copy the full SHA
    f604d66 View commit details
    Browse the repository at this point in the history
  3. Configuration menu
    Copy the full SHA
    7d3f495 View commit details
    Browse the repository at this point in the history

Commits on Jul 2, 2024

  1. Configuration menu
    Copy the full SHA
    6abd384 View commit details
    Browse the repository at this point in the history
  2. Configuration menu
    Copy the full SHA
    ae98923 View commit details
    Browse the repository at this point in the history