From b0201b56d09a2fa4d688ca08be1728798c3ab522 Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Sun, 19 May 2024 12:51:03 -0600 Subject: [PATCH] Fix schema objects with default + nullable --- package.json | 2 +- .../examples/digital-ocean-api.ts | 2 +- .../github-api-export-type-immutable.ts | 6 ++--- .../examples/github-api-immutable.ts | 6 ++--- .../examples/github-api-next.ts | 2 +- .../openapi-typescript/examples/github-api.ts | 6 ++--- .../src/transform/schema-object.ts | 23 ++++++++++------ .../transform/schema-object/string.test.ts | 21 +++++++++++++++ pnpm-lock.yaml | 26 +++++++++---------- 9 files changed, 61 insertions(+), 33 deletions(-) diff --git a/package.json b/package.json index 886ff0752..4f48fd44b 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "devDependencies": { "@biomejs/biome": "^1.7.3", "@changesets/changelog-github": "^0.5.0", - "@changesets/cli": "^2.27.1", + "@changesets/cli": "^2.27.2", "del-cli": "^5.1.0", "prettier": "^3.2.5", "typescript": "^5.4.5" diff --git a/packages/openapi-typescript/examples/digital-ocean-api.ts b/packages/openapi-typescript/examples/digital-ocean-api.ts index 856fffeea..c2b5cc948 100644 --- a/packages/openapi-typescript/examples/digital-ocean-api.ts +++ b/packages/openapi-typescript/examples/digital-ocean-api.ts @@ -9894,7 +9894,7 @@ export interface components { * "web" * ] */ - tags: string[] | null; + tags: string[]; /** * @description A string containing 'user data' which may be used to configure the Droplet on first boot, often a 'cloud-config' file or Bash script. It must be plain text and may not exceed 64 KiB in size. * @example #cloud-config diff --git a/packages/openapi-typescript/examples/github-api-export-type-immutable.ts b/packages/openapi-typescript/examples/github-api-export-type-immutable.ts index 66e9399b6..dc8a11d0b 100644 --- a/packages/openapi-typescript/examples/github-api-export-type-immutable.ts +++ b/packages/openapi-typescript/examples/github-api-export-type-immutable.ts @@ -29148,7 +29148,7 @@ export type components = { * @default RIGHT * @enum {string|null} */ - readonly start_side: "LEFT" | "RIGHT" | null; + readonly start_side: "LEFT" | "RIGHT"; /** * @description The line of the blob to which the comment applies. The last line of the range for a multi-line comment * @example 2 @@ -30255,7 +30255,7 @@ export type components = { * @default RIGHT * @enum {string|null} */ - readonly start_side: "LEFT" | "RIGHT" | null; + readonly start_side: "LEFT" | "RIGHT"; /** * @description The line of the blob to which the comment applies. The last line of the range for a multi-line comment * @example 2 @@ -101600,7 +101600,7 @@ export interface operations { /** @description The name of the task for the deployment (e.g., `deploy` or `deploy:migrations`). */ readonly task?: string; /** @description The name of the environment that was deployed to (e.g., `staging` or `production`). */ - readonly environment?: string | null; + readonly environment?: string; /** @description The number of results per page (max 100). For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ readonly per_page?: components["parameters"]["per-page"]; /** @description The page number of the results to fetch. For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ diff --git a/packages/openapi-typescript/examples/github-api-immutable.ts b/packages/openapi-typescript/examples/github-api-immutable.ts index 6b6c022a8..20beab493 100644 --- a/packages/openapi-typescript/examples/github-api-immutable.ts +++ b/packages/openapi-typescript/examples/github-api-immutable.ts @@ -29148,7 +29148,7 @@ export interface components { * @default RIGHT * @enum {string|null} */ - readonly start_side: "LEFT" | "RIGHT" | null; + readonly start_side: "LEFT" | "RIGHT"; /** * @description The line of the blob to which the comment applies. The last line of the range for a multi-line comment * @example 2 @@ -30255,7 +30255,7 @@ export interface components { * @default RIGHT * @enum {string|null} */ - readonly start_side: "LEFT" | "RIGHT" | null; + readonly start_side: "LEFT" | "RIGHT"; /** * @description The line of the blob to which the comment applies. The last line of the range for a multi-line comment * @example 2 @@ -101600,7 +101600,7 @@ export interface operations { /** @description The name of the task for the deployment (e.g., `deploy` or `deploy:migrations`). */ readonly task?: string; /** @description The name of the environment that was deployed to (e.g., `staging` or `production`). */ - readonly environment?: string | null; + readonly environment?: string; /** @description The number of results per page (max 100). For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ readonly per_page?: components["parameters"]["per-page"]; /** @description The page number of the results to fetch. For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ diff --git a/packages/openapi-typescript/examples/github-api-next.ts b/packages/openapi-typescript/examples/github-api-next.ts index dd16bb079..3b69f07d0 100644 --- a/packages/openapi-typescript/examples/github-api-next.ts +++ b/packages/openapi-typescript/examples/github-api-next.ts @@ -101899,7 +101899,7 @@ export interface operations { /** @description The name of the task for the deployment (e.g., `deploy` or `deploy:migrations`). */ task?: string; /** @description The name of the environment that was deployed to (e.g., `staging` or `production`). */ - environment?: string | null; + environment?: string; /** @description The number of results per page (max 100). For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ per_page?: components["parameters"]["per-page"]; /** @description The page number of the results to fetch. For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ diff --git a/packages/openapi-typescript/examples/github-api.ts b/packages/openapi-typescript/examples/github-api.ts index bef5f3112..23917b2f5 100644 --- a/packages/openapi-typescript/examples/github-api.ts +++ b/packages/openapi-typescript/examples/github-api.ts @@ -29148,7 +29148,7 @@ export interface components { * @default RIGHT * @enum {string|null} */ - start_side: "LEFT" | "RIGHT" | null; + start_side: "LEFT" | "RIGHT"; /** * @description The line of the blob to which the comment applies. The last line of the range for a multi-line comment * @example 2 @@ -30255,7 +30255,7 @@ export interface components { * @default RIGHT * @enum {string|null} */ - start_side: "LEFT" | "RIGHT" | null; + start_side: "LEFT" | "RIGHT"; /** * @description The line of the blob to which the comment applies. The last line of the range for a multi-line comment * @example 2 @@ -101600,7 +101600,7 @@ export interface operations { /** @description The name of the task for the deployment (e.g., `deploy` or `deploy:migrations`). */ task?: string; /** @description The name of the environment that was deployed to (e.g., `staging` or `production`). */ - environment?: string | null; + environment?: string; /** @description The number of results per page (max 100). For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ per_page?: components["parameters"]["per-page"]; /** @description The page number of the results to fetch. For more information, see "[Using pagination in the REST API](https://docs.github.com/rest/using-the-rest-api/using-pagination-in-the-rest-api)." */ diff --git a/packages/openapi-typescript/src/transform/schema-object.ts b/packages/openapi-typescript/src/transform/schema-object.ts index 68646e24b..dd4a4bbf0 100644 --- a/packages/openapi-typescript/src/transform/schema-object.ts +++ b/packages/openapi-typescript/src/transform/schema-object.ts @@ -116,7 +116,10 @@ export function transformSchemaObjectWithComposition( return ts.factory.createTypeReferenceNode(enumType.name); } const enumType = schemaObject.enum.map(tsLiteral); - if ((Array.isArray(schemaObject.type) && schemaObject.type.includes("null")) || schemaObject.nullable) { + if ( + ((Array.isArray(schemaObject.type) && schemaObject.type.includes("null")) || schemaObject.nullable) && + !schemaObject.default + ) { enumType.push(NULL); } @@ -242,7 +245,7 @@ export function transformSchemaObjectWithComposition( // if final type could be generated, return intersection of all members if (finalType) { // deprecated nullable - if (schemaObject.nullable) { + if (schemaObject.nullable && !schemaObject.default) { return tsNullable([finalType]); } return finalType; @@ -364,7 +367,7 @@ function transformSchemaObjectCore(schemaObject: SchemaObject, options: Transfor // polymorphic, or 3.1 nullable if (Array.isArray(schemaObject.type) && !Array.isArray(schemaObject)) { // skip any primitive types that appear in oneOf as well - let uniqueTypes: ts.TypeNode[] = []; + const uniqueTypes: ts.TypeNode[] = []; if (Array.isArray(schemaObject.oneOf)) { for (const t of schemaObject.type) { if ( @@ -383,11 +386,15 @@ function transformSchemaObjectCore(schemaObject: SchemaObject, options: Transfor ); } } else { - uniqueTypes = schemaObject.type.map((t) => - t === "null" || t === null - ? NULL - : transformSchemaObject({ ...schemaObject, type: t } as SchemaObject, options), - ); + for (const t of schemaObject.type) { + if (t === "null" || t === null) { + if (!schemaObject.default) { + uniqueTypes.push(NULL); + } + } else { + uniqueTypes.push(transformSchemaObject({ ...schemaObject, type: t } as SchemaObject, options)); + } + } } return tsUnion(uniqueTypes); } diff --git a/packages/openapi-typescript/test/transform/schema-object/string.test.ts b/packages/openapi-typescript/test/transform/schema-object/string.test.ts index 9c22ecd28..e8acac4a6 100644 --- a/packages/openapi-typescript/test/transform/schema-object/string.test.ts +++ b/packages/openapi-typescript/test/transform/schema-object/string.test.ts @@ -91,6 +91,27 @@ describe("transformSchemaObject > string", () => { want: '"A" | "B" | "C" | null', }, ], + [ + "default + nullable", + { + given: { type: ["string", "null"], default: "en" }, + want: "string", + }, + ], + [ + "default + nullable + enum", + { + given: { type: ["string", "null"], enum: ["en", "es", "fr", "de"], default: "en" }, + want: '"en" | "es" | "fr" | "de"', + }, + ], + [ + "default + nullable (deprecated syntax)", + { + given: { type: "string", default: "en", nullable: true }, + want: "string", + }, + ], ]; for (const [testName, { given, want, options = DEFAULT_OPTIONS, ci }] of tests) { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f747a1859..a62dbf4c8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^0.5.0 version: 0.5.0 '@changesets/cli': - specifier: ^2.27.1 - version: 2.27.1 + specifier: ^2.27.2 + version: 2.27.2 del-cli: specifier: ^5.1.0 version: 5.1.0 @@ -426,8 +426,8 @@ packages: '@bundled-es-modules/statuses@1.0.1': resolution: {integrity: sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==} - '@changesets/apply-release-plan@7.0.0': - resolution: {integrity: sha512-vfi69JR416qC9hWmFGSxj7N6wA5J222XNBmezSVATPWDVPIF7gkd4d8CpbEbXmRWbVrkoli3oerGS6dcL/BGsQ==} + '@changesets/apply-release-plan@7.0.1': + resolution: {integrity: sha512-aPdSq/R++HOyfEeBGjEe6LNG8gs0KMSyRETD/J2092OkNq8mOioAxyKjMbvVUdzgr/HTawzMOz7lfw339KnsCA==} '@changesets/assemble-release-plan@6.0.0': resolution: {integrity: sha512-4QG7NuisAjisbW4hkLCmGW2lRYdPrKzro+fCtZaILX+3zdUELSvYjpL4GTv0E4aM9Mef3PuIQp89VmHJ4y2bfw==} @@ -438,8 +438,8 @@ packages: '@changesets/changelog-github@0.5.0': resolution: {integrity: sha512-zoeq2LJJVcPJcIotHRJEEA2qCqX0AQIeFE+L21L8sRLPVqDhSXY8ZWAt2sohtBpFZkBwu+LUwMSKRr2lMy3LJA==} - '@changesets/cli@2.27.1': - resolution: {integrity: sha512-iJ91xlvRnnrJnELTp4eJJEOPjgpF3NOh4qeQehM6Ugiz9gJPRZ2t+TsXun6E3AMN4hScZKjqVXl0TX+C7AB3ZQ==} + '@changesets/cli@2.27.2': + resolution: {integrity: sha512-6/kADjKMOrlLwNr/Y5HAq7T9oGOA2Lq5A59AGtwQCCiXuSGp4EgszzdJFeBiF8pdz7Wn1HaLzSUBhAaNToEJqg==} hasBin: true '@changesets/config@3.0.0': @@ -481,8 +481,8 @@ packages: '@changesets/types@6.0.0': resolution: {integrity: sha512-b1UkfNulgKoWfqyHtzKS5fOZYSJO+77adgL7DLRDr+/7jhChN+QcHnbjiQVOz/U+Ts3PGNySq7diAItzDgugfQ==} - '@changesets/write@0.3.0': - resolution: {integrity: sha512-slGLb21fxZVUYbyea+94uFiD6ntQW0M2hIKNznFizDhZPDgn2c/fv1UzzlW43RVzh1BEDuIqW6hzlJ1OflNmcw==} + '@changesets/write@0.3.1': + resolution: {integrity: sha512-SyGtMXzH3qFqlHKcvFY2eX+6b0NGiFcNav8AFsYwy5l8hejOeoeTDemu5Yjmke2V5jpzY+pBvM0vCCQ3gdZpfw==} '@docsearch/css@3.6.0': resolution: {integrity: sha512-+sbxb71sWre+PwDK7X2T8+bhS6clcVMLwBPznX45Qu6opJcgRjAp7gYSDzVFp187J+feSj5dNBN1mJoi6ckkUQ==} @@ -3572,7 +3572,7 @@ snapshots: dependencies: statuses: 2.0.1 - '@changesets/apply-release-plan@7.0.0': + '@changesets/apply-release-plan@7.0.1': dependencies: '@babel/runtime': 7.24.5 '@changesets/config': 3.0.0 @@ -3609,10 +3609,10 @@ snapshots: transitivePeerDependencies: - encoding - '@changesets/cli@2.27.1': + '@changesets/cli@2.27.2': dependencies: '@babel/runtime': 7.24.5 - '@changesets/apply-release-plan': 7.0.0 + '@changesets/apply-release-plan': 7.0.1 '@changesets/assemble-release-plan': 6.0.0 '@changesets/changelog-git': 0.2.0 '@changesets/config': 3.0.0 @@ -3624,7 +3624,7 @@ snapshots: '@changesets/pre': 2.0.0 '@changesets/read': 0.6.0 '@changesets/types': 6.0.0 - '@changesets/write': 0.3.0 + '@changesets/write': 0.3.1 '@manypkg/get-packages': 1.1.3 '@types/semver': 7.5.8 ansi-colors: 4.1.3 @@ -3727,7 +3727,7 @@ snapshots: '@changesets/types@6.0.0': {} - '@changesets/write@0.3.0': + '@changesets/write@0.3.1': dependencies: '@babel/runtime': 7.24.5 '@changesets/types': 6.0.0