fork of hey-api/openapi-ts because I need some additional things
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: make any service operation accept options

Lubos d13cfd26 36424515

+222 -135
+31 -2
examples/openapi-ts-fetch/src/App.tsx
··· 4 4 import { useState } from 'react'; 5 5 6 6 import { $Pet } from './client/schemas.gen'; 7 - import { getPetById } from './client/services.gen'; 7 + import { 8 + findPetsByStatus, 9 + getInventory, 10 + getPetById, 11 + } from './client/services.gen'; 8 12 9 13 createClient({ 10 14 baseUrl: 'https://petstore3.swagger.io/api/v3', ··· 17 21 const onFetchPet = async () => { 18 22 // random id 1-10 19 23 const petId = Math.floor(Math.random() * (10 - 1 + 1) + 1); 20 - const { data: pet } = await getPetById({ 24 + const { data: pet, error } = await getPetById({ 21 25 path: { 22 26 petId, 23 27 }, 24 28 }); 29 + if (error) { 30 + // TODO: discriminate by error status 31 + } 25 32 setPet(pet); 26 33 }; 27 34 35 + const onFindPetsByStatus = () => { 36 + // everything in this call is optional 37 + findPetsByStatus({ 38 + query: { 39 + status: 'pending', 40 + }, 41 + }); 42 + }; 43 + 44 + const onGetInventory = () => { 45 + // this call has no params but could be still customized 46 + getInventory(); 47 + }; 48 + 28 49 return ( 29 50 <> 30 51 <div className="container"> ··· 42 63 Fetch random pet 43 64 </button> 44 65 <span className="pet-name">Fetched pet's name: {pet?.name}</span> 66 + </div> 67 + <div className="flex"> 68 + <button className="button" onClick={onFindPetsByStatus}> 69 + Find pets by status 70 + </button> 71 + <button className="button" onClick={onGetInventory}> 72 + Get inventory 73 + </button> 45 74 </div> 46 75 <div className="openapi-ts"> 47 76 <code>{"import { $Pet } from './client/schemas.gen'"}</code>
+39 -39
examples/openapi-ts-fetch/src/client/services.gen.ts
··· 1 1 // This file is auto-generated by @hey-api/openapi-ts 2 2 3 - import { client } from '@hey-api/client-fetch'; 3 + import { client, type Options } from '@hey-api/client-fetch'; 4 4 5 5 import type { 6 6 AddPetData, ··· 45 45 * @returns Pet Successful operation 46 46 * @throws ApiError 47 47 */ 48 - export const addPet = (data: AddPetData) => 48 + export const addPet = (options: Options<AddPetData>) => 49 49 client.post<AddPetResponse>({ 50 - ...data, 50 + ...options, 51 51 url: '/pet', 52 52 }); 53 53 ··· 59 59 * @returns Pet Successful operation 60 60 * @throws ApiError 61 61 */ 62 - export const updatePet = (data: UpdatePetData) => 62 + export const updatePet = (options: Options<UpdatePetData>) => 63 63 client.put<UpdatePetResponse>({ 64 - ...data, 64 + ...options, 65 65 url: '/pet', 66 66 }); 67 67 ··· 73 73 * @returns Pet successful operation 74 74 * @throws ApiError 75 75 */ 76 - export const findPetsByStatus = (data: FindPetsByStatusData = {}) => 76 + export const findPetsByStatus = (options?: Options<FindPetsByStatusData>) => 77 77 client.get<FindPetsByStatusResponse>({ 78 - ...data, 78 + ...options, 79 79 url: '/pet/findByStatus', 80 80 }); 81 81 ··· 87 87 * @returns Pet successful operation 88 88 * @throws ApiError 89 89 */ 90 - export const findPetsByTags = (data: FindPetsByTagsData = {}) => 90 + export const findPetsByTags = (options?: Options<FindPetsByTagsData>) => 91 91 client.get<FindPetsByTagsResponse>({ 92 - ...data, 92 + ...options, 93 93 url: '/pet/findByTags', 94 94 }); 95 95 ··· 101 101 * @returns Pet successful operation 102 102 * @throws ApiError 103 103 */ 104 - export const getPetById = (data: GetPetByIdData) => 104 + export const getPetById = (options: Options<GetPetByIdData>) => 105 105 client.get<GetPetByIdResponse>({ 106 - ...data, 106 + ...options, 107 107 url: '/pet/{petId}', 108 108 }); 109 109 ··· 115 115 * @param data.status Status of pet that needs to be updated 116 116 * @throws ApiError 117 117 */ 118 - export const updatePetWithForm = (data: UpdatePetWithFormData) => 118 + export const updatePetWithForm = (options: Options<UpdatePetWithFormData>) => 119 119 client.post<void>({ 120 - ...data, 120 + ...options, 121 121 url: '/pet/{petId}', 122 122 }); 123 123 ··· 128 128 * @param data.apiKey 129 129 * @throws ApiError 130 130 */ 131 - export const deletePet = (data: DeletePetData) => 131 + export const deletePet = (options: Options<DeletePetData>) => 132 132 client.delete<void>({ 133 - ...data, 133 + ...options, 134 134 url: '/pet/{petId}', 135 135 }); 136 136 ··· 143 143 * @returns ApiResponse successful operation 144 144 * @throws ApiError 145 145 */ 146 - export const uploadFile = (data: UploadFileData) => 146 + export const uploadFile = (options: Options<UploadFileData>) => 147 147 client.post<UploadFileResponse>({ 148 - ...data, 148 + ...options, 149 149 url: '/pet/{petId}/uploadImage', 150 150 }); 151 151 ··· 155 155 * @returns number successful operation 156 156 * @throws ApiError 157 157 */ 158 - export const getInventory = () => 158 + export const getInventory = (options?: Options) => 159 159 client.get<GetInventoryResponse>({ 160 - ...data, 160 + ...options, 161 161 url: '/store/inventory', 162 162 }); 163 163 ··· 169 169 * @returns Order successful operation 170 170 * @throws ApiError 171 171 */ 172 - export const placeOrder = (data: PlaceOrderData = {}) => 172 + export const placeOrder = (options?: Options<PlaceOrderData>) => 173 173 client.post<PlaceOrderResponse>({ 174 - ...data, 174 + ...options, 175 175 url: '/store/order', 176 176 }); 177 177 ··· 183 183 * @returns Order successful operation 184 184 * @throws ApiError 185 185 */ 186 - export const getOrderById = (data: GetOrderByIdData) => 186 + export const getOrderById = (options: Options<GetOrderByIdData>) => 187 187 client.get<GetOrderByIdResponse>({ 188 - ...data, 188 + ...options, 189 189 url: '/store/order/{orderId}', 190 190 }); 191 191 ··· 196 196 * @param data.orderId ID of the order that needs to be deleted 197 197 * @throws ApiError 198 198 */ 199 - export const deleteOrder = (data: DeleteOrderData) => 199 + export const deleteOrder = (options: Options<DeleteOrderData>) => 200 200 client.delete<void>({ 201 - ...data, 201 + ...options, 202 202 url: '/store/order/{orderId}', 203 203 }); 204 204 ··· 210 210 * @returns User successful operation 211 211 * @throws ApiError 212 212 */ 213 - export const createUser = (data: CreateUserData = {}) => 213 + export const createUser = (options?: Options<CreateUserData>) => 214 214 client.post<CreateUserResponse>({ 215 - ...data, 215 + ...options, 216 216 url: '/user', 217 217 }); 218 218 ··· 226 226 * @throws ApiError 227 227 */ 228 228 export const createUsersWithListInput = ( 229 - data: CreateUsersWithListInputData = {}, 229 + options?: Options<CreateUsersWithListInputData>, 230 230 ) => 231 231 client.post<CreateUsersWithListInputResponse>({ 232 - ...data, 232 + ...options, 233 233 url: '/user/createWithList', 234 234 }); 235 235 ··· 241 241 * @returns string successful operation 242 242 * @throws ApiError 243 243 */ 244 - export const loginUser = (data: LoginUserData = {}) => 244 + export const loginUser = (options?: Options<LoginUserData>) => 245 245 client.get<LoginUserResponse>({ 246 - ...data, 246 + ...options, 247 247 url: '/user/login', 248 248 }); 249 249 ··· 252 252 * @returns unknown successful operation 253 253 * @throws ApiError 254 254 */ 255 - export const logoutUser = () => 255 + export const logoutUser = (options?: Options) => 256 256 client.get<LogoutUserResponse>({ 257 - ...data, 257 + ...options, 258 258 url: '/user/logout', 259 259 }); 260 260 ··· 265 265 * @returns User successful operation 266 266 * @throws ApiError 267 267 */ 268 - export const getUserByName = (data: GetUserByNameData) => 268 + export const getUserByName = (options: Options<GetUserByNameData>) => 269 269 client.get<GetUserByNameResponse>({ 270 - ...data, 270 + ...options, 271 271 url: '/user/{username}', 272 272 }); 273 273 ··· 280 280 * @returns unknown successful operation 281 281 * @throws ApiError 282 282 */ 283 - export const updateUser = (data: UpdateUserData) => 283 + export const updateUser = (options: Options<UpdateUserData>) => 284 284 client.put<UpdateUserResponse>({ 285 - ...data, 285 + ...options, 286 286 url: '/user/{username}', 287 287 }); 288 288 ··· 293 293 * @param data.username The name that needs to be deleted 294 294 * @throws ApiError 295 295 */ 296 - export const deleteUser = (data: DeleteUserData) => 296 + export const deleteUser = (options: Options<DeleteUserData>) => 297 297 client.delete<void>({ 298 - ...data, 298 + ...options, 299 299 url: '/user/{username}', 300 300 });
+2 -2
packages/client-fetch/src/index.ts
··· 17 17 resolve, 18 18 } from '@hey-api/client-core'; 19 19 20 - import type { Config, Req, RequestResponse } from './types'; 20 + import type { Config, Req, RequestResult } from './types'; 21 21 import { 22 22 createDefaultConfig, 23 23 createInterceptors, ··· 264 264 265 265 const request = async <Data = unknown, Error = unknown>( 266 266 options: Req, 267 - ): Promise<RequestResponse<Data, Error>> => { 267 + ): RequestResult<Data, Error> => { 268 268 const config = getConfig(); 269 269 270 270 const opts: Opts = {
+1 -1
packages/client-fetch/src/node/index.ts
··· 1 1 export { client, createClient, request } from '../'; 2 - export type { Config } from '../types'; 2 + export type { Config, Options } from '../types'; 3 3 export type { CancelablePromise } from '@hey-api/client-core';
+7 -1
packages/client-fetch/src/types.ts
··· 23 23 text: Awaited<ReturnType<Response['text']>>; 24 24 } 25 25 26 - export interface RequestResponse<Data = unknown, Error = unknown> { 26 + interface RequestResponse<Data = unknown, Error = unknown> { 27 27 error?: Error; 28 28 data?: Data; 29 29 response: Response; 30 30 } 31 + 32 + export type RequestResult<Data = unknown, Error = unknown> = Promise< 33 + RequestResponse<Data, Error> 34 + >; 31 35 32 36 export interface Config extends FetchConfig { 33 37 /** ··· 58 62 export interface Req 59 63 extends Omit<ApiRequestOptions, 'headers'>, 60 64 Omit<Partial<Config>, 'body' | 'method'> {} 65 + 66 + export type Options<T = unknown> = Omit<Req, 'method' | 'url'> & T;
+31 -31
packages/openapi-ts/src/compiler/module.ts
··· 3 3 import { 4 4 addLeadingJSDocComment, 5 5 type Comments, 6 - type ImportItemObject, 6 + type ImportExportItemObject, 7 7 ots, 8 8 } from './utils'; 9 9 ··· 20 20 ots.string(module), 21 21 ); 22 22 23 - type ImportItem = ImportItemObject | string; 23 + type ImportExportItem = ImportExportItemObject | string; 24 24 25 25 /** 26 26 * Create a named export declaration. Example: `export { X } from './y'`. ··· 29 29 * @returns ExportDeclaration 30 30 */ 31 31 export const createNamedExportDeclarations = ( 32 - items: Array<ImportItem> | ImportItem, 32 + items: Array<ImportExportItem> | ImportExportItem, 33 33 module: string, 34 34 ): ts.ExportDeclaration => { 35 35 items = Array.isArray(items) ? items : [items]; 36 - const isAllTypes = items.every((i) => typeof i === 'object' && i.isTypeOnly); 37 - return ts.factory.createExportDeclaration( 36 + const exportedTypes = Array.isArray(items) ? items : [items]; 37 + const hasNonTypeExport = exportedTypes.some( 38 + (item) => typeof item !== 'object' || !item.asType, 39 + ); 40 + const elements = items.map((name) => { 41 + const item = typeof name === 'string' ? { name } : name; 42 + return ots.export({ 43 + alias: item.alias, 44 + asType: hasNonTypeExport && item.asType, 45 + name: item.name, 46 + }); 47 + }); 48 + const exportClause = ts.factory.createNamedExports(elements); 49 + const moduleSpecifier = ots.string(module); 50 + const statement = ts.factory.createExportDeclaration( 38 51 undefined, 39 - isAllTypes, 40 - ts.factory.createNamedExports( 41 - items.map((item) => { 42 - const { 43 - name, 44 - isTypeOnly = undefined, 45 - alias = undefined, 46 - } = typeof item === 'string' ? { name: item } : item; 47 - return ots.export( 48 - name, 49 - isAllTypes ? false : Boolean(isTypeOnly), 50 - alias, 51 - ); 52 - }), 53 - ), 54 - ots.string(module), 52 + !hasNonTypeExport, 53 + exportClause, 54 + moduleSpecifier, 55 55 ); 56 + return statement; 56 57 }; 57 58 58 59 /** ··· 103 104 * @returns ImportDeclaration 104 105 */ 105 106 export const createNamedImportDeclarations = ( 106 - items: Array<ImportItem> | ImportItem, 107 + items: Array<ImportExportItem> | ImportExportItem, 107 108 module: string, 108 109 ): ts.ImportDeclaration => { 109 110 const importedTypes = Array.isArray(items) ? items : [items]; 110 - const isTypeOnly = !importedTypes.some( 111 - (item) => typeof item !== 'object' || !item.isTypeOnly, 111 + const hasNonTypeImport = importedTypes.some( 112 + (item) => typeof item !== 'object' || !item.asType, 112 113 ); 113 - const elements = importedTypes.map((item) => { 114 - const importedType: ImportItemObject = 115 - typeof item === 'string' ? { name: item } : item; 114 + const elements = importedTypes.map((name) => { 115 + const item = typeof name === 'string' ? { name } : name; 116 116 return ots.import({ 117 - alias: importedType.alias, 118 - isTypeOnly: isTypeOnly ? false : Boolean(importedType.isTypeOnly), 119 - name: importedType.name, 117 + alias: item.alias, 118 + asType: hasNonTypeImport && item.asType, 119 + name: item.name, 120 120 }); 121 121 }); 122 122 const namedBindings = ts.factory.createNamedImports(elements); 123 123 const importClause = ts.factory.createImportClause( 124 - isTypeOnly, 124 + !hasNonTypeImport, 125 125 undefined, 126 126 namedBindings, 127 127 );
+25 -16
packages/openapi-ts/src/compiler/utils.ts
··· 3 3 import { getConfig } from '../utils/config'; 4 4 import { unescapeName } from '../utils/escape'; 5 5 6 - export interface ImportItemObject { 6 + export interface ImportExportItemObject { 7 7 alias?: string; 8 - isTypeOnly?: boolean; 8 + asType?: boolean; 9 9 name: string; 10 10 } 11 11 ··· 81 81 return file.statements[0]; 82 82 } 83 83 84 - // ots for openapi-ts is helpers to reduce repetition of basic ts factory functions. 84 + /** 85 + * ots for openapi-ts are helpers to reduce repetition of basic TypeScript 86 + * factory functions. 87 + */ 85 88 export const ots = { 86 - // Create a boolean expression based on value. 89 + /** 90 + * Create a boolean expression based on value. 91 + */ 87 92 boolean: (value: boolean) => 88 93 value ? ts.factory.createTrue() : ts.factory.createFalse(), 89 - export: (name: string, isTypeOnly?: boolean, alias?: string) => { 90 - const n = ts.factory.createIdentifier(encodeURIComponent(name)); 91 - return ts.factory.createExportSpecifier( 92 - isTypeOnly ?? false, 93 - alias ? n : undefined, 94 - alias ? ts.factory.createIdentifier(encodeURIComponent(alias)) : n, 95 - ); 94 + export: ({ alias, asType = false, name }: ImportExportItemObject) => { 95 + const nameNode = ts.factory.createIdentifier(name); 96 + if (alias) { 97 + const aliasNode = ts.factory.createIdentifier(alias); 98 + return ts.factory.createExportSpecifier(asType, nameNode, aliasNode); 99 + } 100 + return ts.factory.createExportSpecifier(asType, undefined, nameNode); 96 101 }, 97 - import: ({ alias, isTypeOnly = false, name }: ImportItemObject) => { 102 + import: ({ alias, asType = false, name }: ImportExportItemObject) => { 98 103 const nameNode = ts.factory.createIdentifier(name); 99 104 if (alias) { 100 105 const aliasNode = ts.factory.createIdentifier(alias); 101 - return ts.factory.createImportSpecifier(isTypeOnly, nameNode, aliasNode); 106 + return ts.factory.createImportSpecifier(asType, nameNode, aliasNode); 102 107 } 103 - return ts.factory.createImportSpecifier(isTypeOnly, undefined, nameNode); 108 + return ts.factory.createImportSpecifier(asType, undefined, nameNode); 104 109 }, 105 - // Create a numeric expression, handling negative numbers. 110 + /** 111 + * Create a numeric expression, handling negative numbers. 112 + */ 106 113 number: (value: number) => { 107 114 if (value < 0) { 108 115 return ts.factory.createPrefixUnaryExpression( ··· 112 119 } 113 120 return ts.factory.createNumericLiteral(value); 114 121 }, 115 - // Create a string literal. This handles strings that start with '`' or "'". 122 + /** 123 + * Create a string literal. This handles strings that start with '`' or "'". 124 + */ 116 125 string: (value: string, unescape = false) => { 117 126 let text = value; 118 127 if (unescape) {
+2 -2
packages/openapi-ts/src/utils/write/index.ts
··· 17 17 if (config.services.response === 'response') { 18 18 files.index.add( 19 19 compiler.export.named( 20 - { isTypeOnly: true, name: 'ApiResult' }, 20 + { asType: true, name: 'ApiResult' }, 21 21 './core/ApiResult', 22 22 ), 23 23 ); ··· 37 37 } 38 38 files.index.add( 39 39 compiler.export.named( 40 - ['OpenAPI', { isTypeOnly: true, name: 'OpenAPIConfig' }], 40 + ['OpenAPI', { asType: true, name: 'OpenAPIConfig' }], 41 41 './core/OpenAPI', 42 42 ), 43 43 );
+52 -32
packages/openapi-ts/src/utils/write/services.ts
··· 39 39 } 40 40 41 41 const { name } = uniqueTypeName({ meta, ...uniqueTypeNameArgs }); 42 - onImport(name); 42 + if (name) { 43 + onImport(name); 44 + } 43 45 }; 44 46 45 47 export const operationDataTypeName = (name: string) => ··· 52 54 client: Client, 53 55 operation: Operation, 54 56 ): FunctionParameter[] => { 55 - if (!operation.parameters.length) { 56 - return []; 57 - } 58 - 59 57 const config = getConfig(); 60 58 61 59 const { name: importedType } = uniqueTypeName({ ··· 69 67 nameTransformer: operationDataTypeName, 70 68 }); 71 69 72 - if (config.useOptions) { 73 - const isRequired = operation.parameters.some( 74 - (parameter) => parameter.isRequired, 75 - ); 70 + const isRequired = operation.parameters.some( 71 + (parameter) => parameter.isRequired, 72 + ); 73 + 74 + if (config.client.startsWith('@hey-api')) { 76 75 return [ 77 76 { 78 - default: isRequired ? undefined : {}, 79 - name: 'data', 80 - type: importedType, 77 + isRequired, 78 + name: 'options', 79 + type: importedType ? `Options<${importedType}>` : 'Options', 81 80 }, 82 81 ]; 83 82 } 84 83 85 - return operation.parameters.map((p) => { 86 - const typePath = `${importedType}['${p.name}']`; 87 - return { 88 - default: p?.default, 89 - isRequired: modelIsRequired(p) === '', 90 - name: p.name, 91 - type: typePath, 92 - }; 93 - }); 84 + if (!operation.parameters.length) { 85 + return []; 86 + } 87 + 88 + // legacy configuration 89 + if (!config.useOptions) { 90 + return operation.parameters.map((p) => { 91 + const typePath = `${importedType}['${p.name}']`; 92 + return { 93 + default: p?.default, 94 + isRequired: modelIsRequired(p) === '', 95 + name: p.name, 96 + type: typePath, 97 + }; 98 + }); 99 + } 100 + 101 + return [ 102 + { 103 + default: isRequired ? undefined : {}, 104 + name: 'data', 105 + type: importedType, 106 + }, 107 + ]; 94 108 }; 95 109 96 110 const toOperationReturnType = (client: Client, operation: Operation) => { ··· 167 181 if (config.client.startsWith('@hey-api')) { 168 182 const obj: ObjectValue[] = [ 169 183 { 170 - spread: 'data', 184 + spread: 'options', 171 185 }, 172 186 { 173 187 key: 'url', ··· 283 297 compiler.return.functionCall({ 284 298 args: [options], 285 299 name: `client.${operation.method.toLocaleLowerCase()}`, 286 - types: [returnType], 300 + types: returnType ? [returnType] : [], 287 301 }), 288 302 ]; 289 303 } ··· 453 467 454 468 // Import required packages and core files. 455 469 if (config.client.startsWith('@hey-api')) { 456 - files.services?.addImport(['client'], config.client); 470 + files.services?.addImport( 471 + [ 472 + 'client', 473 + { 474 + asType: true, 475 + name: 'Options', 476 + }, 477 + ], 478 + config.client, 479 + ); 457 480 } else { 458 481 if (config.client === 'angular') { 459 482 files.services?.addImport('Injectable', '@angular/core'); ··· 462 485 files.services?.addImport('HttpClient', '@angular/common/http'); 463 486 } 464 487 465 - files.services?.addImport( 466 - { isTypeOnly: true, name: 'Observable' }, 467 - 'rxjs', 468 - ); 488 + files.services?.addImport({ asType: true, name: 'Observable' }, 'rxjs'); 469 489 } else { 470 490 files.services?.addImport( 471 - { isTypeOnly: true, name: 'CancelablePromise' }, 491 + { asType: true, name: 'CancelablePromise' }, 472 492 './core/CancelablePromise', 473 493 ); 474 494 } 475 495 476 496 if (config.services.response === 'response') { 477 497 files.services?.addImport( 478 - { isTypeOnly: true, name: 'ApiResult' }, 498 + { asType: true, name: 'ApiResult' }, 479 499 './core/ApiResult', 480 500 ); 481 501 } 482 502 483 503 if (config.name) { 484 504 files.services?.addImport( 485 - { isTypeOnly: config.client !== 'angular', name: 'BaseHttpRequest' }, 505 + { asType: config.client !== 'angular', name: 'BaseHttpRequest' }, 486 506 './core/BaseHttpRequest', 487 507 ); 488 508 } else { ··· 498 518 if (files.types && !files.types.isEmpty()) { 499 519 const importedTypes = imports 500 520 .filter(unique) 501 - .map((name) => ({ isTypeOnly: true, name })); 521 + .map((name) => ({ asType: true, name })); 502 522 files.services?.addImport(importedTypes, `./${files.types.getName(false)}`); 503 523 } 504 524 };
+22 -7
packages/openapi-ts/src/utils/write/type.ts
··· 144 144 * Generates a unique name for the exported type for given model meta. 145 145 * @param args.client Internal client instance 146 146 * @param args.count Unique key for deduplication 147 + * @param args.create If a name record does not exist, should it be created? 147 148 * @param args.meta Meta property from the model 148 149 * @param args.nameTransformer Function for transforming name into the final 149 150 * value. In different contexts, a different strategy might be used. For ··· 154 155 export const uniqueTypeName = ({ 155 156 client, 156 157 count = 1, 158 + create = false, 157 159 meta, 158 160 nameTransformer, 159 161 }: Pick<Required<Model>, 'meta'> & { 160 162 client: Client; 161 163 count?: number; 164 + create?: boolean; 162 165 nameTransformer?: (value: string) => string; 163 166 }): UniqueTypeNameResult => { 164 167 let result: UniqueTypeNameResult = { 165 168 created: false, 166 - name: meta.name, 169 + name: '', 167 170 }; 171 + let name = meta.name; 168 172 if (nameTransformer) { 169 - result.name = nameTransformer(result.name); 173 + name = nameTransformer(name); 170 174 } 171 175 if (count > 1) { 172 - result.name = `${result.name}${count}`; 176 + name = `${name}${count}`; 173 177 } 174 - const type = client.types[result.name]; 178 + const type = client.types[name]; 175 179 if (!type) { 176 - client.types[result.name] = meta; 177 - result.created = true; 178 - } else if (type.$ref !== meta.$ref) { 180 + if (create) { 181 + client.types[name] = meta; 182 + result = { 183 + created: true, 184 + name, 185 + }; 186 + } 187 + } else if (type.$ref === meta.$ref) { 188 + result = { 189 + created: false, 190 + name, 191 + }; 192 + } else { 179 193 result = uniqueTypeName({ 180 194 client, 181 195 count: count + 1, 196 + create, 182 197 meta, 183 198 nameTransformer, 184 199 });
+10 -2
packages/openapi-ts/src/utils/write/types.ts
··· 53 53 return; 54 54 } 55 55 56 - const { created, name } = uniqueTypeName({ meta, ...uniqueTypeNameArgs }); 56 + const { created, name } = uniqueTypeName({ 57 + create: true, 58 + meta, 59 + ...uniqueTypeNameArgs, 60 + }); 57 61 if (created) { 58 62 const node = compiler.types.enum({ 59 63 comments, ··· 83 87 return; 84 88 } 85 89 86 - const { created, name } = uniqueTypeName({ meta, ...uniqueTypeNameArgs }); 90 + const { created, name } = uniqueTypeName({ 91 + create: true, 92 + meta, 93 + ...uniqueTypeNameArgs, 94 + }); 87 95 if (created) { 88 96 const node = compiler.typedef.alias({ comment, name, type }); 89 97 onNode(node);