From 1885cad234ed9207c097b4fd3e1b9279ed4b39bf Mon Sep 17 00:00:00 2001 From: Mark Probst Date: Mon, 20 Jul 2026 16:36:36 -0400 Subject: [PATCH] fix(typescript-input): strip brace-wrapped types from JSDoc @type tags (#2695) typescript-json-schema treats a property's `@type {string}` JSDoc tag as a "type" validation keyword and copies its raw text verbatim, braces included, into the generated schema's `type` field. That produces invalid JSON Schema like `"type": "{string}"`, which also makes quicktype fail hard when that schema is later consumed for any other output language. Sanitize the schema after generateSchema() runs: strip a single pair of surrounding braces from any string `type` value. Co-Authored-By: gpt-5.6-sol via pi --- .../quicktype-typescript-input/src/index.ts | 26 +++++++++++++++++++ test/unit/typescript-input.test.ts | 17 ++++++++++++ 2 files changed, 43 insertions(+) diff --git a/packages/quicktype-typescript-input/src/index.ts b/packages/quicktype-typescript-input/src/index.ts index e29f49118f..f94edb4007 100644 --- a/packages/quicktype-typescript-input/src/index.ts +++ b/packages/quicktype-typescript-input/src/index.ts @@ -125,6 +125,30 @@ function patchGeneratorForBuiltinTypes( }; } +// typescript-json-schema copies JSDoc `@type {string}` annotations into the +// generated schema verbatim. Strip the JSDoc braces so the value is a valid +// JSON Schema type. +function sanitizeTypeAnnotations(value: unknown): void { + if (Array.isArray(value)) { + value.forEach(sanitizeTypeAnnotations); + return; + } + + if (value === null || typeof value !== "object") { + return; + } + + const schema = value as Record; + if (typeof schema.type === "string") { + const match = /^\{([^{}]+)\}$/.exec(schema.type); + if (match !== null) { + schema.type = match[1]; + } + } + + Object.values(schema).forEach(sanitizeTypeAnnotations); +} + // FIXME: We're stringifying and then parsing this schema again. Just pass around // the schema directly. export function schemaForTypeScriptSources( @@ -151,6 +175,8 @@ export function schemaForTypeScriptSources( patchGeneratorForBuiltinTypes(generator, program); const schema = generateSchema(program, "*", settings, undefined, generator); + sanitizeTypeAnnotations(schema); + const uris: string[] = []; let topLevelName = ""; diff --git a/test/unit/typescript-input.test.ts b/test/unit/typescript-input.test.ts index 2e5f6a8842..dedb3335d2 100644 --- a/test/unit/typescript-input.test.ts +++ b/test/unit/typescript-input.test.ts @@ -84,6 +84,23 @@ describe("schemaForTypeScriptSources", () => { expect(schema.definitions.Person.type).toBe("object"); }); + // https://github.com/glideapps/quicktype/issues/2695 + test("strips braces from JSDoc type annotations", () => { + const { schema } = schemaForSource(` + export type Person = { + /** + * @type {string} + * @memberOf {Person} + */ + name: string; + }; + + export type MemberInfo = Required; + `); + + expect(schema.definitions.Person.properties.name.type).toBe("string"); + }); + test("class property initializers become defaults", () => { const { schema } = schemaForSource(` export class Config {