Identical properties with same `title` generate multiple types
#510 opened on Feb 14, 2023
Repository metrics
- Stars
- (3,302 stars)
- PR merge metrics
- (No merged PRs in 30d)
Description
Example: A rectangle schema with two corners. Both corners are the same type and most importantly share a title.
I know this isn't an ideal schema (should ref in the points), but sometimes you just get bad data from somewhere.
import { compile } from 'json-schema-to-typescript';
import * as fs from 'node:fs';
const schema = {
type: "object",
title: "rectangle",
properties: {
p1: {
type: "object",
title: "point",
properties: {
x: "number",
y: "number"
},
required: ["x", "y"]
},
p2: {
type: "object",
title: "point",
properties: {
x: "number",
y: "number"
},
required: ["x", "y"]
}
},
required: ["p1", "p2"]
}
fs.writeFileSync("dupe-example.ts", await compile(schema, undefined, { additionalProperties: false, bannerComment: "" }))
this turns into
export interface Rectangle {
p1: Point;
p2: Point1;
}
export interface Point {
x: "number";
y: "number";
}
export interface Point1 {
x: "number";
y: "number";
}
which is correct, I guess? after all, I'm putting garbage in and get garbage out. I'd prefer to get something like
export interface Rectangle {
p1: Point;
p2: Point;
}
export interface Point {
x: "number";
y: "number";
}
but I'm not sure if that's in the scope of this project. I saw #456, but as far as I understand it, that only allows changing the interface name before1 the incrementing number is appended, not after (so the same as just setting the title).
Ideally, I'd want a way to hard override the generated interface name. "Trust me, this is a Point, don't complain about duplicates"2.
As a more complex alternative, this package could "detect" identical properties (with or without matching titles) and reuse the interfacces generated from those. Again, this might be out of scope - we're starting to shift from parse+generate into ✨ magic ✨
Any advice? I'd also be fine with a hacky workaround, can't be worse than regexing through my .ts file3 :^)
Footnotes
-
see https://github.com/bcherny/json-schema-to-typescript/pull/456/files#diff-abe2818e9c29aadf84401cb7ef8286c7cbce9253eba09f6fa52e22ca4e57b17eR303-R305 -
generateNameis called after the override and adds a counter ↩ -
I'm already committing a kind of crime here: I take in lots of schemas and compile them individually, all into one file. I run ts-dedupe on that file which gets rid of interfaces with the same name. This helps a lot when multiple schemas define a
"title": "point"somewhere, but can't duplicates within a schema by itself. ts-dedupe only compares interface names, not their contents/definitions. ↩ -
As a final note: I don't have to have neat types, since I can just create my own
Pointinterface and use that everywhere. After all, typescript will let identical types from different definitions "fit onto" each other. As long as nobody looks at the schemas' type definitions, the 10 differentPointXXinterfaces aren't noticable ↩