diff --git a/src/openApi/v3/interfaces/Extensions/WithEnumExtension.d.ts b/src/openApi/v3/interfaces/Extensions/WithEnumExtension.d.ts index 0d6ead780..e57ffcd39 100644 --- a/src/openApi/v3/interfaces/Extensions/WithEnumExtension.d.ts +++ b/src/openApi/v3/interfaces/Extensions/WithEnumExtension.d.ts @@ -1,4 +1,4 @@ export interface WithEnumExtension { - 'x-enum-varnames'?: string[]; 'x-enum-descriptions'?: string[]; + 'x-enum-varnames'?: string[]; } diff --git a/src/openApi/v3/interfaces/OpenApiSchema.d.ts b/src/openApi/v3/interfaces/OpenApiSchema.d.ts index ff1b63b59..354ce5c6a 100644 --- a/src/openApi/v3/interfaces/OpenApiSchema.d.ts +++ b/src/openApi/v3/interfaces/OpenApiSchema.d.ts @@ -9,51 +9,51 @@ import type { OpenApiXml } from './OpenApiXml'; * https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#schemaObject */ export interface OpenApiSchema extends OpenApiReference, WithEnumExtension { - title?: string; - multipleOf?: number; - maximum?: number; - exclusiveMaximum?: boolean; - minimum?: number; - exclusiveMinimum?: boolean; - maxLength?: number; - minLength?: number; - pattern?: string; - maxItems?: number; - minItems?: number; - uniqueItems?: boolean; - maxProperties?: number; - minProperties?: number; - required?: string[]; - enum?: (string | number)[]; - type?: string | string[]; - const?: string | number | boolean | null; + additionalProperties?: boolean | OpenApiSchema; allOf?: OpenApiSchema[]; - oneOf?: OpenApiSchema[]; anyOf?: OpenApiSchema[]; - not?: OpenApiSchema[]; - items?: OpenApiSchema; - properties?: Dictionary; - additionalProperties?: boolean | OpenApiSchema; + const?: string | number | boolean | null; + default?: any; + deprecated?: boolean; description?: string; + discriminator?: OpenApiDiscriminator; + enum?: (string | number)[]; + example?: any; + exclusiveMaximum?: boolean; + exclusiveMinimum?: boolean; + externalDocs?: OpenApiExternalDocs; format?: - | 'int32' - | 'int64' - | 'float' - | 'double' - | 'string' + | 'binary' | 'boolean' | 'byte' - | 'binary' - | 'date' | 'date-time' - | 'password'; - default?: any; + | 'date' + | 'double' + | 'float' + | 'int32' + | 'int64' + | 'password' + | 'string'; + items?: OpenApiSchema; + maximum?: number; + maxItems?: number; + maxLength?: number; + maxProperties?: number; + minimum?: number; + minItems?: number; + minLength?: number; + minProperties?: number; + multipleOf?: number; + not?: OpenApiSchema[]; nullable?: boolean; - discriminator?: OpenApiDiscriminator; + oneOf?: OpenApiSchema[]; + pattern?: string; + properties?: Dictionary; readOnly?: boolean; + required?: string[]; + title?: string; + type?: string | string[]; + uniqueItems?: boolean; writeOnly?: boolean; xml?: OpenApiXml; - externalDocs?: OpenApiExternalDocs; - example?: any; - deprecated?: boolean; } diff --git a/src/openApi/v3/parser/getModel.ts b/src/openApi/v3/parser/getModel.ts index 8c79d8397..80b7e8daa 100644 --- a/src/openApi/v3/parser/getModel.ts +++ b/src/openApi/v3/parser/getModel.ts @@ -17,45 +17,45 @@ export const getModel = ( parentDefinition: OpenApiSchema | null = null ): Model => { const model: Model = { - name, - export: 'interface', - type: 'any', base: 'any', - template: null, - link: null, + deprecated: Boolean(definition.deprecated), description: definition.description || null, - deprecated: definition.deprecated === true, + enum: [], + enums: [], + exclusiveMaximum: definition.exclusiveMaximum, + exclusiveMinimum: definition.exclusiveMinimum, + export: 'interface', + format: definition.format, + imports: [], isDefinition, - isReadOnly: definition.readOnly === true, isNullable: definition.nullable === true, + isReadOnly: definition.readOnly === true, isRequired: false, - format: definition.format, + link: null, maximum: definition.maximum, - exclusiveMaximum: definition.exclusiveMaximum, - minimum: definition.minimum, - exclusiveMinimum: definition.exclusiveMinimum, - multipleOf: definition.multipleOf, - maxLength: definition.maxLength, - minLength: definition.minLength, maxItems: definition.maxItems, - minItems: definition.minItems, - uniqueItems: definition.uniqueItems, + maxLength: definition.maxLength, maxProperties: definition.maxProperties, + minimum: definition.minimum, + minItems: definition.minItems, + minLength: definition.minLength, minProperties: definition.minProperties, + multipleOf: definition.multipleOf, + name, pattern: getPattern(definition.pattern), - imports: [], - enum: [], - enums: [], properties: [], + template: null, + type: 'any', + uniqueItems: definition.uniqueItems, }; if (definition.$ref) { const definitionRef = getType(definition.$ref); - model.export = 'reference'; - model.type = definitionRef.type; model.base = definitionRef.base; - model.template = definitionRef.template; + model.export = 'reference'; model.imports.push(...definitionRef.imports); + model.template = definitionRef.template; + model.type = definitionRef.type; model.default = getModelDefault(definition, model); return model; } @@ -64,10 +64,10 @@ export const getModel = ( const enumerators = getEnum(definition.enum); const extendedEnumerators = extendEnum(enumerators, definition); if (extendedEnumerators.length) { - model.export = 'enum'; - model.type = 'string'; model.base = 'string'; model.enum.push(...extendedEnumerators); + model.export = 'enum'; + model.type = 'string'; model.default = getModelDefault(definition, model); return model; } @@ -76,11 +76,11 @@ export const getModel = ( if (definition.type === 'array' && definition.items) { if (definition.items.$ref) { const arrayItems = getType(definition.items.$ref); - model.export = 'array'; - model.type = arrayItems.type; model.base = arrayItems.base; - model.template = arrayItems.template; + model.export = 'array'; model.imports.push(...arrayItems.imports); + model.template = arrayItems.template; + model.type = arrayItems.type; model.default = getModelDefault(definition, model); return model; } @@ -93,12 +93,12 @@ export const getModel = ( } const arrayItems = getModel(openApi, definition.items); - model.export = 'array'; - model.type = arrayItems.type; model.base = arrayItems.base; - model.template = arrayItems.template; - model.link = arrayItems; + model.export = 'array'; model.imports.push(...arrayItems.imports); + model.link = arrayItems; + model.template = arrayItems.template; + model.type = arrayItems.type; model.default = getModelDefault(definition, model); return model; } @@ -117,15 +117,15 @@ export const getModel = ( if (definition.type === 'object') { if (definition.properties) { + model.base = 'any'; model.export = 'interface'; model.type = 'any'; - model.base = 'any'; model.default = getModelDefault(definition, model); const modelProperties = getModelProperties(openApi, definition, getModel, model); modelProperties.forEach(modelProperty => { - model.imports.push(...modelProperty.imports); model.enums.push(...modelProperty.enums); + model.imports.push(...modelProperty.imports); model.properties.push(modelProperty); if (modelProperty.export === 'enum') { model.enums.push(modelProperty); @@ -144,23 +144,23 @@ export const getModel = ( } if (definition.const !== undefined) { - model.export = 'const'; const definitionConst = definition.const; const modelConst = typeof definitionConst === 'string' ? `"${definitionConst}"` : `${definitionConst}`; - model.type = modelConst; model.base = modelConst; + model.export = 'const'; + model.type = modelConst; return model; } // If the schema has a type than it can be a basic or generic type. if (definition.type) { const definitionType = getType(definition.type, definition.format); - model.export = 'generic'; - model.type = definitionType.type; model.base = definitionType.base; - model.template = definitionType.template; - model.isNullable = definitionType.isNullable || model.isNullable; + model.export = 'generic'; model.imports.push(...definitionType.imports); + model.isNullable = definitionType.isNullable || model.isNullable; + model.template = definitionType.template; + model.type = definitionType.type; model.default = getModelDefault(definition, model); return model; } diff --git a/src/openApi/v3/parser/getModelComposition.ts b/src/openApi/v3/parser/getModelComposition.ts index fcace09c0..7132feb14 100644 --- a/src/openApi/v3/parser/getModelComposition.ts +++ b/src/openApi/v3/parser/getModelComposition.ts @@ -99,21 +99,21 @@ export const getModelComposition = ({ }); } else { composition.properties.push({ - name: 'properties', - export: 'interface', - type: 'any', base: 'any', - template: null, - link: null, description: '', + enum: [], + enums: [], + export: 'interface', + imports: [], isDefinition: false, - isReadOnly: false, isNullable: false, + isReadOnly: false, isRequired: false, - imports: [], - enum: [], - enums: [], + link: null, + name: 'properties', properties, + template: null, + type: 'any', }); } } diff --git a/src/templates/partials/exportInterface.hbs b/src/templates/partials/exportInterface.hbs index f94c0197e..2dc99472b 100644 --- a/src/templates/partials/exportInterface.hbs +++ b/src/templates/partials/exportInterface.hbs @@ -22,7 +22,7 @@ export type {{{name}}} = { {{/ifdef}} {{>isReadOnly}}{{{name}}}{{>isRequired}}: {{>type parent=../name}}; {{/each}} -}; +}{{>isNullable}}; {{#if enums}} {{#unless @root.useUnionTypes}} diff --git a/test/__snapshots__/index.spec.ts.snap b/test/__snapshots__/index.spec.ts.snap index 650ea2e6c..fc379814a 100644 --- a/test/__snapshots__/index.spec.ts.snap +++ b/test/__snapshots__/index.spec.ts.snap @@ -4051,6 +4051,7 @@ export { ModelWithEnumWithHyphen } from './models/ModelWithEnumWithHyphen'; export type { ModelWithInteger } from './models/ModelWithInteger'; export type { ModelWithNestedEnums } from './models/ModelWithNestedEnums'; export type { ModelWithNestedProperties } from './models/ModelWithNestedProperties'; +export type { ModelWithNullableObject } from './models/ModelWithNullableObject'; export type { ModelWithNullableString } from './models/ModelWithNullableString'; export type { ModelWithOrderedProperties } from './models/ModelWithOrderedProperties'; export type { ModelWithPattern } from './models/ModelWithPattern'; @@ -4059,6 +4060,7 @@ export type { ModelWithReference } from './models/ModelWithReference'; export type { ModelWithString } from './models/ModelWithString'; export type { NestedAnyOfArraysNullable } from './models/NestedAnyOfArraysNullable'; export type { NonAsciiStringæøåÆØÅöôêÊ字符串 } from './models/NonAsciiStringæøåÆØÅöôêÊ字符串'; +export type { NullableObject } from './models/NullableObject'; export type { Pageable } from './models/Pageable'; export type { SimpleBoolean } from './models/SimpleBoolean'; export type { SimpleFile } from './models/SimpleFile'; @@ -4134,6 +4136,7 @@ export { $ModelWithEnumWithHyphen } from './schemas/$ModelWithEnumWithHyphen'; export { $ModelWithInteger } from './schemas/$ModelWithInteger'; export { $ModelWithNestedEnums } from './schemas/$ModelWithNestedEnums'; export { $ModelWithNestedProperties } from './schemas/$ModelWithNestedProperties'; +export { $ModelWithNullableObject } from './schemas/$ModelWithNullableObject'; export { $ModelWithNullableString } from './schemas/$ModelWithNullableString'; export { $ModelWithOrderedProperties } from './schemas/$ModelWithOrderedProperties'; export { $ModelWithPattern } from './schemas/$ModelWithPattern'; @@ -4142,6 +4145,7 @@ export { $ModelWithReference } from './schemas/$ModelWithReference'; export { $ModelWithString } from './schemas/$ModelWithString'; export { $NestedAnyOfArraysNullable } from './schemas/$NestedAnyOfArraysNullable'; export { $NonAsciiStringæøåÆØÅöôêÊ字符串 } from './schemas/$NonAsciiStringæøåÆØÅöôêÊ字符串'; +export { $NullableObject } from './schemas/$NullableObject'; export { $Pageable } from './schemas/$Pageable'; export { $SimpleBoolean } from './schemas/$SimpleBoolean'; export { $SimpleFile } from './schemas/$SimpleFile'; @@ -5251,6 +5255,19 @@ export type ModelWithNestedProperties = { " `; +exports[`v3 should generate: test/generated/v3/models/ModelWithNullableObject.ts 1`] = ` +"/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +import type { NullableObject } from './NullableObject'; +export type ModelWithNullableObject = { + data?: NullableObject; +}; + +" +`; + exports[`v3 should generate: test/generated/v3/models/ModelWithNullableString.ts 1`] = ` "/* generated using openapi-typescript-codegen -- do not edit */ /* istanbul ignore file */ @@ -5406,6 +5423,21 @@ export type NonAsciiStringæøåÆØÅöôêÊ字符串 = string; " `; +exports[`v3 should generate: test/generated/v3/models/NullableObject.ts 1`] = ` +"/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +/** + * An object that can be null + */ +export type NullableObject = { + foo?: string; +} | null; + +" +`; + exports[`v3 should generate: test/generated/v3/models/Pageable.ts 1`] = ` "/* generated using openapi-typescript-codegen -- do not edit */ /* istanbul ignore file */ @@ -6882,6 +6914,21 @@ export const $ModelWithNestedProperties = { " `; +exports[`v3 should generate: test/generated/v3/schemas/$ModelWithNullableObject.ts 1`] = ` +"/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $ModelWithNullableObject = { + properties: { + data: { + type: 'NullableObject', + }, + }, +} as const; +" +`; + exports[`v3 should generate: test/generated/v3/schemas/$ModelWithNullableString.ts 1`] = ` "/* generated using openapi-typescript-codegen -- do not edit */ /* istanbul ignore file */ @@ -7121,6 +7168,23 @@ export const $NonAsciiStringæøåÆØÅöôêÊ字符串 = { " `; +exports[`v3 should generate: test/generated/v3/schemas/$NullableObject.ts 1`] = ` +"/* generated using openapi-typescript-codegen -- do not edit */ +/* istanbul ignore file */ +/* tslint:disable */ +/* eslint-disable */ +export const $NullableObject = { + description: \`An object that can be null\`, + properties: { + foo: { + type: 'string', + }, + }, + isNullable: true, +} as const; +" +`; + exports[`v3 should generate: test/generated/v3/schemas/$Pageable.ts 1`] = ` "/* generated using openapi-typescript-codegen -- do not edit */ /* istanbul ignore file */ diff --git a/test/spec/v3.json b/test/spec/v3.json index c939aed37..2e1e78b23 100644 --- a/test/spec/v3.json +++ b/test/spec/v3.json @@ -2881,6 +2881,24 @@ "minimum": 0.0 } } + }, + "NullableObject": { + "type": "object", + "nullable": true, + "description": "An object that can be null", + "properties": { + "foo": { + "type": "string" + } + } + }, + "ModelWithNullableObject": { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/NullableObject" + } + } } } }