Skip to content

Schema $ref to standalone type results in generation of type in generated typescript interface. #141

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

Closed
AndyDBell opened this issue Jan 17, 2018 · 10 comments

Comments

@AndyDBell
Copy link

I am not sure whether this is a configuration problem, expected behaviour or a bug but...

I declare a json schema file id.schema.json containing:

{
  "title": "TId",
  "type": "string"
}

Running json-schema-to-typescript.compileFromFile( id.schema.json ) generates:

export type TId = string

I create a second json schema file example.schema.json referencing id.schema.json and containing:

{
  "title": "IExample",
  "type": "object",
  "properties": {
    "id": {
      "$ref": "id.schema.json"
    },
    "name": {
      "type": "string"
    }
  },
  "required":[
    "id"
  ],
  "additionalProperties": false
}

Running json-schema-to-typescript.compileFromFile( example.schema.json ) generates:

export type TId = string

export interface IExample {
  id: TId
  name?: string
}

Here the type TId is generated in addition to the interface IExample. i.e. TId is defined twice, once in each file.

This makes it impossible to use the generated code in a typescript program with out manual editing to remove the second definition of TId.

@bcherny
Copy link
Owner

bcherny commented Jan 17, 2018

Hi @AndyDBell! You can avoid declaring externally referenced types by setting the declareExternallyReferenced option to false. In the future, you'll be able to use batch mode to generate a set of typings at once with automatic deduping.

Let me know if that works for your use case!

@bcherny bcherny closed this as completed Jan 17, 2018
@bcherny bcherny self-assigned this Jan 17, 2018
@AndyDBell
Copy link
Author

Hi Boris,

Thanks for your response.

I have tried setting declareExternallyReferenced to false in the compileFromFile options but the output did not change!

This is my options object:

  const options: any = {
    cwd: './src/models/jsonschema',
    declareExternallyReferenced: false,
    unreachableDefinitions: false,
    style: {
      semi: false,
      singleQuote: true,
    }
  }

The generated typescript is the same as with declareExternallyReferenced set to true.

@bcherny
Copy link
Owner

bcherny commented Jan 17, 2018

Hmm could you upload a self-contained repro case? This does seem like a bug. In the meantime, generating multiple copies of a type should be fine - TypeScript will merge them for you as best it can.

@AndyDBell
Copy link
Author

Zipped self contained repro code shared on onedrive at https://1drv.ms/u/s!ArpiIInQBexRhZY9b-gSPf3z1QxXTQ.
Note - included everything including node_modules, dist etc. Editor is vscode.

@bcherny
Copy link
Owner

bcherny commented Jan 17, 2018

Thanks for the repro case @AndyDBell.

What's happening here is that declareExternallyReferenced isn't working as expected for primitive types (as opposed to interfaces).

I maintain this project in my spare time, and won't have time to fix this soon. Sorry about the inconvenience. Also, contributions are welcome!

In the meantime, you have 3 options for workarounds: (1) remove the type alias from your JSON-Schema, (2) manually edit the outputted file, or (3) combine your schemas into one.

@AndyDBell
Copy link
Author

AndyDBell commented Jan 17, 2018 via email

@adamduren
Copy link

@AndyDBell A workaround I'm currently using is to use the the tsType extension to name the type instead of $referencing it. You just have to make sure in whatever script you are using to generate the type definitions includes the type once. Hope this helps.

@Pomaio
Copy link

Pomaio commented Nov 17, 2020

I fixed this problem in my repo with this code: (its not ready solution but work for me)

read json:

const sources = await Promise.all(
    schemas
      .filter(v => v.endsWith('json'))
      .map(async v => {
        const dereferenced = await refParser.dereference(
          _pathToJSON_
        );
        return  await compile(dereferenced, v.replace('.json', ''), _options_);
      })
  );

and filtred existing interfaces

// get first nesting level
const floatSources = sources.reduce((acc, s) => [...acc, ...s.split('export').filter(v => !!v && /(interface|enum|type)/g.test(v)).map(v => 'export' + v)], []);  

const getInterfaceName = (source) => source.match(/export (interface|enum|type) (\w+)/)[2];

let existingInterfaces = [];

const filtredSources = floatSources.filter((source) => {
  const interfaceName = getInterfaceName(source);
  if (existingInterfaces.includes(interfaceName)) {
    return false
  }
  existingInterfaces.push(interfaceName);
  return true;
});

@Roaders
Copy link

Roaders commented Mar 31, 2021

declareExternallyReferenced nearly fixed my use case except that there were no imports. In my example ICompany references IEmployee. In the generated code Iemployee was not added to the d.ts file for ICompany but there was no import for IEmployee so it fails.

@bcherny
Copy link
Owner

bcherny commented Jun 25, 2024

Merging into #258

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants