From 5bc3a596c7e7f2ee243b36a6cd1eab7f1ed6db33 Mon Sep 17 00:00:00 2001 From: owjs3901 Date: Fri, 17 Apr 2026 18:02:31 +0900 Subject: [PATCH] Fix wild key issue --- .../changepack_log_PICQGAdJ559UmGGsfIaks.json | 1 + .../src/__tests__/generate-interface.test.ts | 52 +++++++++++++++++++ .../wrap-interface-key-guard.test.ts | 8 +++ .../generator/src/wrap-interface-key-guard.ts | 5 ++ 4 files changed, 66 insertions(+) create mode 100644 .changepacks/changepack_log_PICQGAdJ559UmGGsfIaks.json diff --git a/.changepacks/changepack_log_PICQGAdJ559UmGGsfIaks.json b/.changepacks/changepack_log_PICQGAdJ559UmGGsfIaks.json new file mode 100644 index 0000000..f9b3caf --- /dev/null +++ b/.changepacks/changepack_log_PICQGAdJ559UmGGsfIaks.json @@ -0,0 +1 @@ +{"changes":{"packages/generator/package.json":"Patch"},"note":"Fix wild key issue","date":"2026-04-17T09:02:19.475575400Z"} \ No newline at end of file diff --git a/packages/generator/src/__tests__/generate-interface.test.ts b/packages/generator/src/__tests__/generate-interface.test.ts index 3800489..638c033 100644 --- a/packages/generator/src/__tests__/generate-interface.test.ts +++ b/packages/generator/src/__tests__/generate-interface.test.ts @@ -2532,3 +2532,55 @@ test('generateInterface preserves JSON endpoints alongside form/multipart endpoi // Raw multipart should use FormData | Record expect(result).toContain('FormData | Record') }) + +test('generateInterface emits index signature for additionalProperties (not quoted key)', () => { + const schema = { + paths: { + '/news-categories': { + post: { + operationId: 'createNewsCategory', + requestBody: { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/NewsCategoryCreateRequest', + }, + }, + }, + }, + responses: { + '201': { + description: 'Created', + content: { + 'application/json': { + schema: { type: 'string' as const }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: { + NewsCategoryCreateRequest: { + type: 'object' as const, + properties: { + title: { + type: 'object' as const, + properties: {}, + required: [], + additionalProperties: { type: 'string' as const }, + }, + }, + required: ['title'], + }, + }, + }, + } + const result = generateInterface(createSchemas(createDocument(schema as any))) + // Must emit a real TS index signature + expect(result).toContain('[key: string]: string') + // Must NOT emit a quoted literal key + expect(result).not.toContain("'[key: string]'") +}) diff --git a/packages/generator/src/__tests__/wrap-interface-key-guard.test.ts b/packages/generator/src/__tests__/wrap-interface-key-guard.test.ts index 29c3774..37a8062 100644 --- a/packages/generator/src/__tests__/wrap-interface-key-guard.test.ts +++ b/packages/generator/src/__tests__/wrap-interface-key-guard.test.ts @@ -67,3 +67,11 @@ test.each([ ] as const)('wrapInterfaceKeyGuard handles optional keys (ending with ?): %s -> %s', (key, expected) => { expect(wrapInterfaceKeyGuard(key)).toBe(expected) }) + +test.each([ + ['[key: string]', '[key: string]'], + ['[key: number]', '[key: number]'], + ['[k: string]', '[k: string]'], +] as const)('wrapInterfaceKeyGuard preserves index signature syntax: %s -> %s', (key, expected) => { + expect(wrapInterfaceKeyGuard(key)).toBe(expected) +}) diff --git a/packages/generator/src/wrap-interface-key-guard.ts b/packages/generator/src/wrap-interface-key-guard.ts index 31f9e1c..b7ad613 100644 --- a/packages/generator/src/wrap-interface-key-guard.ts +++ b/packages/generator/src/wrap-interface-key-guard.ts @@ -4,6 +4,11 @@ export function wrapInterfaceKeyGuard(key: string): string { return key } + // Preserve TypeScript index signature syntax (e.g., [key: string], [key: number]) + if (/^\[.+:\s*.+\]$/.test(key)) { + return key + } + // Check if key ends with '?' (optional marker in TypeScript) // If so, process the base key and add '?' back at the end const isOptional = key.endsWith('?')