diff --git a/src/normalizer.ts b/src/normalizer.ts index 6ba6c006..29cd8217 100644 --- a/src/normalizer.ts +++ b/src/normalizer.ts @@ -145,6 +145,30 @@ rules.set('Transform const to singleton enum', schema => { } }) +rules.set('Transform nullable -> anyOf', schema => { + if (schema.nullable === true) { + delete schema.nullable + + const copiedSchema = {...schema} + + // This stuff should not be in `anyOf`. + delete copiedSchema.id + delete copiedSchema.nullable + + // This stuff will be in `anyOf` instead. + delete schema.format + delete schema.type + + schema.anyOf = [ + copiedSchema, + // @ts-expect-error + { + type: 'null' + } + ] + } +}) + export function normalize(rootSchema: LinkedJSONSchema, filename: string, options: Options): NormalizedJSONSchema { rules.forEach(rule => traverse(rootSchema, schema => rule(schema, filename, options))) return rootSchema as NormalizedJSONSchema diff --git a/test/__snapshots__/test/test.ts.md b/test/__snapshots__/test/test.ts.md index 7e03a2c8..0232b3c6 100644 --- a/test/__snapshots__/test/test.ts.md +++ b/test/__snapshots__/test/test.ts.md @@ -5682,6 +5682,142 @@ Generated by [AVA](https://avajs.dev). }␊ ` +## realWorld.jsonschema.js + +> Expected output to match snapshot for e2e test: realWorld.jsonschema.js + + `/* tslint:disable */␊ + /**␊ + * This file was automatically generated by json-schema-to-typescript.␊ + * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊ + * and run json-schema-to-typescript to regenerate this file.␊ + */␊ + ␊ + export type CoreSchemaMetaSchema = CoreSchemaMetaSchema1 & CoreSchemaMetaSchema2;␊ + export type NonNegativeInteger = number;␊ + export type NonNegativeIntegerDefault0 = NonNegativeInteger;␊ + export type CoreSchemaMetaSchema2 =␊ + | {␊ + $id?: string;␊ + $schema?: string;␊ + $ref?: string;␊ + $comment?: string;␊ + title?: string;␊ + description?: string;␊ + default?: true;␊ + readOnly?: boolean;␊ + writeOnly?: boolean;␊ + examples?: true[];␊ + multipleOf?: number;␊ + maximum?: number;␊ + exclusiveMaximum?: number;␊ + minimum?: number;␊ + exclusiveMinimum?: number;␊ + maxLength?: NonNegativeInteger;␊ + minLength?: NonNegativeIntegerDefault0;␊ + pattern?: string;␊ + additionalItems?: CoreSchemaMetaSchema2;␊ + items?: CoreSchemaMetaSchema2 | SchemaArray;␊ + maxItems?: NonNegativeInteger;␊ + minItems?: NonNegativeIntegerDefault0;␊ + uniqueItems?: boolean;␊ + contains?: CoreSchemaMetaSchema2;␊ + maxProperties?: NonNegativeInteger;␊ + minProperties?: NonNegativeIntegerDefault0;␊ + required?: StringArray;␊ + additionalProperties?: CoreSchemaMetaSchema2;␊ + definitions?: {␊ + [k: string]: CoreSchemaMetaSchema2;␊ + };␊ + properties?: {␊ + [k: string]: CoreSchemaMetaSchema2;␊ + };␊ + patternProperties?: {␊ + [k: string]: CoreSchemaMetaSchema2;␊ + };␊ + dependencies?: {␊ + [k: string]: CoreSchemaMetaSchema2 | StringArray;␊ + };␊ + propertyNames?: CoreSchemaMetaSchema2;␊ + const?: true;␊ + enum?: [true, ...unknown[]];␊ + type?: SimpleTypes | [SimpleTypes, ...SimpleTypes[]];␊ + format?: string;␊ + contentMediaType?: string;␊ + contentEncoding?: string;␊ + if?: CoreSchemaMetaSchema2;␊ + then?: CoreSchemaMetaSchema2;␊ + else?: CoreSchemaMetaSchema2;␊ + allOf?: SchemaArray;␊ + anyOf?: SchemaArray;␊ + oneOf?: SchemaArray;␊ + not?: CoreSchemaMetaSchema2;␊ + [k: string]: unknown;␊ + }␊ + | boolean;␊ + export type SchemaArray = [CoreSchemaMetaSchema2, ...CoreSchemaMetaSchema2[]];␊ + export type StringArray = string[];␊ + export type SimpleTypes = "array" | "boolean" | "integer" | "null" | "number" | "object" | "string";␊ + ␊ + export interface CoreSchemaMetaSchema1 {␊ + $id?: string;␊ + $schema?: string;␊ + $ref?: string;␊ + $comment?: string;␊ + title?: string;␊ + description?: string;␊ + default?: true;␊ + readOnly?: boolean;␊ + writeOnly?: boolean;␊ + examples?: true[];␊ + multipleOf?: number;␊ + maximum?: number;␊ + exclusiveMaximum?: number;␊ + minimum?: number;␊ + exclusiveMinimum?: number;␊ + maxLength?: NonNegativeInteger;␊ + minLength?: NonNegativeIntegerDefault0;␊ + pattern?: string;␊ + additionalItems?: CoreSchemaMetaSchema2;␊ + items?: CoreSchemaMetaSchema2 | SchemaArray;␊ + maxItems?: NonNegativeInteger;␊ + minItems?: NonNegativeIntegerDefault0;␊ + uniqueItems?: boolean;␊ + contains?: CoreSchemaMetaSchema2;␊ + maxProperties?: NonNegativeInteger;␊ + minProperties?: NonNegativeIntegerDefault0;␊ + required?: StringArray;␊ + additionalProperties?: CoreSchemaMetaSchema2;␊ + definitions?: {␊ + [k: string]: CoreSchemaMetaSchema2;␊ + };␊ + properties?: {␊ + [k: string]: CoreSchemaMetaSchema2;␊ + };␊ + patternProperties?: {␊ + [k: string]: CoreSchemaMetaSchema2;␊ + };␊ + dependencies?: {␊ + [k: string]: CoreSchemaMetaSchema2 | StringArray;␊ + };␊ + propertyNames?: CoreSchemaMetaSchema2;␊ + const?: true;␊ + enum?: [true, ...unknown[]];␊ + type?: SimpleTypes | [SimpleTypes, ...SimpleTypes[]];␊ + format?: string;␊ + contentMediaType?: string;␊ + contentEncoding?: string;␊ + if?: CoreSchemaMetaSchema2;␊ + then?: CoreSchemaMetaSchema2;␊ + else?: CoreSchemaMetaSchema2;␊ + allOf?: SchemaArray;␊ + anyOf?: SchemaArray;␊ + oneOf?: SchemaArray;␊ + not?: CoreSchemaMetaSchema2;␊ + [k: string]: unknown;␊ + }␊ + ` + ## realWorld.openapi.js > Expected output to match snapshot for e2e test: realWorld.openapi.js @@ -8314,6 +8450,17 @@ Generated by [AVA](https://avajs.dev). "required": []␊ }` +## Normalize empty const to singleton enum + +> Snapshot 1 + + `{␊ + "id": "foo",␊ + "enum": [␊ + ""␊ + ]␊ + }` + ## Non object items.items > Snapshot 1 @@ -8346,6 +8493,25 @@ Generated by [AVA](https://avajs.dev). "additionalProperties": true␊ }` +## Normalize nullable to anyOf + +> Snapshot 1 + + `{␊ + "id": "foo",␊ + "format": "int32",␊ + "anyOf": [␊ + {␊ + "type": "integer",␊ + "format": "int32"␊ + },␊ + {␊ + "type": "null",␊ + "additionalProperties": false␊ + }␊ + ]␊ + }` + ## Remove `enum=[null]` if `type=['null']` > Snapshot 1 @@ -8874,150 +9040,3 @@ Generated by [AVA](https://avajs.dev). "additionalProperties": false,␊ "required": []␊ }` - -## realWorld.jsonschema.js - -> Expected output to match snapshot for e2e test: realWorld.jsonschema.js - - `/* tslint:disable */␊ - /**␊ - * This file was automatically generated by json-schema-to-typescript.␊ - * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,␊ - * and run json-schema-to-typescript to regenerate this file.␊ - */␊ - ␊ - export type CoreSchemaMetaSchema = CoreSchemaMetaSchema1 & CoreSchemaMetaSchema2;␊ - export type NonNegativeInteger = number;␊ - export type NonNegativeIntegerDefault0 = NonNegativeInteger;␊ - export type CoreSchemaMetaSchema2 =␊ - | {␊ - $id?: string;␊ - $schema?: string;␊ - $ref?: string;␊ - $comment?: string;␊ - title?: string;␊ - description?: string;␊ - default?: true;␊ - readOnly?: boolean;␊ - writeOnly?: boolean;␊ - examples?: true[];␊ - multipleOf?: number;␊ - maximum?: number;␊ - exclusiveMaximum?: number;␊ - minimum?: number;␊ - exclusiveMinimum?: number;␊ - maxLength?: NonNegativeInteger;␊ - minLength?: NonNegativeIntegerDefault0;␊ - pattern?: string;␊ - additionalItems?: CoreSchemaMetaSchema2;␊ - items?: CoreSchemaMetaSchema2 | SchemaArray;␊ - maxItems?: NonNegativeInteger;␊ - minItems?: NonNegativeIntegerDefault0;␊ - uniqueItems?: boolean;␊ - contains?: CoreSchemaMetaSchema2;␊ - maxProperties?: NonNegativeInteger;␊ - minProperties?: NonNegativeIntegerDefault0;␊ - required?: StringArray;␊ - additionalProperties?: CoreSchemaMetaSchema2;␊ - definitions?: {␊ - [k: string]: CoreSchemaMetaSchema2;␊ - };␊ - properties?: {␊ - [k: string]: CoreSchemaMetaSchema2;␊ - };␊ - patternProperties?: {␊ - [k: string]: CoreSchemaMetaSchema2;␊ - };␊ - dependencies?: {␊ - [k: string]: CoreSchemaMetaSchema2 | StringArray;␊ - };␊ - propertyNames?: CoreSchemaMetaSchema2;␊ - const?: true;␊ - enum?: [true, ...unknown[]];␊ - type?: SimpleTypes | [SimpleTypes, ...SimpleTypes[]];␊ - format?: string;␊ - contentMediaType?: string;␊ - contentEncoding?: string;␊ - if?: CoreSchemaMetaSchema2;␊ - then?: CoreSchemaMetaSchema2;␊ - else?: CoreSchemaMetaSchema2;␊ - allOf?: SchemaArray;␊ - anyOf?: SchemaArray;␊ - oneOf?: SchemaArray;␊ - not?: CoreSchemaMetaSchema2;␊ - [k: string]: unknown;␊ - }␊ - | boolean;␊ - export type SchemaArray = [CoreSchemaMetaSchema2, ...CoreSchemaMetaSchema2[]];␊ - export type StringArray = string[];␊ - export type SimpleTypes = "array" | "boolean" | "integer" | "null" | "number" | "object" | "string";␊ - ␊ - export interface CoreSchemaMetaSchema1 {␊ - $id?: string;␊ - $schema?: string;␊ - $ref?: string;␊ - $comment?: string;␊ - title?: string;␊ - description?: string;␊ - default?: true;␊ - readOnly?: boolean;␊ - writeOnly?: boolean;␊ - examples?: true[];␊ - multipleOf?: number;␊ - maximum?: number;␊ - exclusiveMaximum?: number;␊ - minimum?: number;␊ - exclusiveMinimum?: number;␊ - maxLength?: NonNegativeInteger;␊ - minLength?: NonNegativeIntegerDefault0;␊ - pattern?: string;␊ - additionalItems?: CoreSchemaMetaSchema2;␊ - items?: CoreSchemaMetaSchema2 | SchemaArray;␊ - maxItems?: NonNegativeInteger;␊ - minItems?: NonNegativeIntegerDefault0;␊ - uniqueItems?: boolean;␊ - contains?: CoreSchemaMetaSchema2;␊ - maxProperties?: NonNegativeInteger;␊ - minProperties?: NonNegativeIntegerDefault0;␊ - required?: StringArray;␊ - additionalProperties?: CoreSchemaMetaSchema2;␊ - definitions?: {␊ - [k: string]: CoreSchemaMetaSchema2;␊ - };␊ - properties?: {␊ - [k: string]: CoreSchemaMetaSchema2;␊ - };␊ - patternProperties?: {␊ - [k: string]: CoreSchemaMetaSchema2;␊ - };␊ - dependencies?: {␊ - [k: string]: CoreSchemaMetaSchema2 | StringArray;␊ - };␊ - propertyNames?: CoreSchemaMetaSchema2;␊ - const?: true;␊ - enum?: [true, ...unknown[]];␊ - type?: SimpleTypes | [SimpleTypes, ...SimpleTypes[]];␊ - format?: string;␊ - contentMediaType?: string;␊ - contentEncoding?: string;␊ - if?: CoreSchemaMetaSchema2;␊ - then?: CoreSchemaMetaSchema2;␊ - else?: CoreSchemaMetaSchema2;␊ - allOf?: SchemaArray;␊ - anyOf?: SchemaArray;␊ - oneOf?: SchemaArray;␊ - not?: CoreSchemaMetaSchema2;␊ - [k: string]: unknown;␊ - }␊ - ` - -## Normalize empty const to singleton enum - -> Snapshot 1 - - `{␊ - "id": "foo",␊ - "enum": [␊ - ""␊ - ]␊ - }` diff --git a/test/__snapshots__/test/test.ts.snap b/test/__snapshots__/test/test.ts.snap index 32b63915..55d0b4bd 100644 Binary files a/test/__snapshots__/test/test.ts.snap and b/test/__snapshots__/test/test.ts.snap differ diff --git a/test/normalizer/nullableToAnyOf.json b/test/normalizer/nullableToAnyOf.json new file mode 100644 index 00000000..27660944 --- /dev/null +++ b/test/normalizer/nullableToAnyOf.json @@ -0,0 +1,21 @@ +{ + "name": "Normalize nullable to anyOf", + "in": { + "id": "foo", + "type": "integer", + "format": "int32", + "nullable": true + }, + "out": { + "id": "foo", + "anyOf": [ + { + "type": "integer", + "format": "int32" + }, + { + "type": "null" + } + ] + } +}