From 93d7b074851c1c61417a6e87e636c08f2dd1ba20 Mon Sep 17 00:00:00 2001 From: Konrad Schultz Date: Tue, 26 Aug 2025 10:14:03 -0700 Subject: [PATCH 1/2] typescript-fetch: Fix model date crash Fix a runtime crash converting to json when a date or datetime is both nullable AND required. There are 4 cases to account for: | required | nullable | values | |----------|----------|--------| | f | f | string OR undefined | | f | t | string OR null OR undefined | | t | f | string | | t | t | string OR null | And importantly when required and nullable code that would crash on null was being generated. additionally when required is false and nullable is true we still want to allow consumers to be able to pass in `undefined` OR `null` and pass that value to the server. Some servers treat null and undefined differently for some operations so having that ability is pretty reasonable. fix: https://github.com/OpenAPITools/openapi-generator/issues/21820 --- .../src/main/resources/typescript-fetch/modelGeneric.mustache | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/typescript-fetch/modelGeneric.mustache b/modules/openapi-generator/src/main/resources/typescript-fetch/modelGeneric.mustache index b28503d5dcb2..cef7f6bd1959 100644 --- a/modules/openapi-generator/src/main/resources/typescript-fetch/modelGeneric.mustache +++ b/modules/openapi-generator/src/main/resources/typescript-fetch/modelGeneric.mustache @@ -141,10 +141,10 @@ export function {{classname}}ToJSONTyped(value?: {{#hasReadOnly}}Omit<{{classnam {{^isReadOnly}} {{#isPrimitiveType}} {{#isDateType}} - '{{baseName}}': {{^required}}{{#isNullable}}value['{{name}}'] === null ? null : {{/isNullable}}{{^isNullable}}value['{{name}}'] == null ? undefined : {{/isNullable}}{{/required}}((value['{{name}}']{{#isNullable}} as any{{/isNullable}}){{^required}}{{#isNullable}}?{{/isNullable}}{{/required}}.toISOString().substring(0,10)), + '{{baseName}}': {{^required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{#isNullable}}{{#required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{/isNullable}}value['{{name}}'].toISOString().substring(0,10), {{/isDateType}} {{#isDateTimeType}} - '{{baseName}}': {{^required}}{{#isNullable}}value['{{name}}'] === null ? null : {{/isNullable}}{{^isNullable}}value['{{name}}'] == null ? undefined : {{/isNullable}}{{/required}}((value['{{name}}']{{#isNullable}} as any{{/isNullable}}){{^required}}{{#isNullable}}?{{/isNullable}}{{/required}}.toISOString()), + '{{baseName}}': {{^required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{#isNullable}}{{#required}}value['{{name}}'] == null ? value['{{name}}'] : {{/required}}{{/isNullable}}value['{{name}}'].toISOString(), {{/isDateTimeType}} {{#isArray}} '{{baseName}}': {{#uniqueItems}}{{^required}}value['{{name}}'] == null ? undefined : {{/required}}{{#required}}{{#isNullable}}value['{{name}}'] == null ? null : {{/isNullable}}{{/required}}Array.from(value['{{name}}'] as Set){{/uniqueItems}}{{^uniqueItems}}value['{{name}}']{{/uniqueItems}}, From 613e58f4e6cf4583e966c2dac28fbcf3d669aaa5 Mon Sep 17 00:00:00 2001 From: Konrad Schultz Date: Tue, 26 Aug 2025 10:24:43 -0700 Subject: [PATCH 2/2] Update typescript-fetch samples ./bin/generate-samples.sh ./bin/configs/*.yaml || exit --- .../typescript-fetch/builds/default-v3.0/models/FormatTest.ts | 4 ++-- .../models/MixedPropertiesAndAdditionalPropertiesClass.ts | 2 +- .../builds/default-v3.0/models/NullableClass.ts | 4 ++-- .../typescript-fetch/builds/default-v3.0/models/Order.ts | 2 +- .../petstore/typescript-fetch/builds/default/models/Order.ts | 2 +- .../typescript-fetch/builds/es6-target/src/models/Order.ts | 2 +- .../builds/multiple-parameters/models/Order.ts | 2 +- .../builds/prefix-parameter-interfaces/src/models/Order.ts | 2 +- .../builds/sagas-and-records/src/models/Order.ts | 2 +- .../builds/snakecase-discriminator/models/FormatTest.ts | 4 ++-- .../models/MixedPropertiesAndAdditionalPropertiesClass.ts | 2 +- .../builds/snakecase-discriminator/models/NullableClass.ts | 4 ++-- .../builds/snakecase-discriminator/models/Order.ts | 2 +- .../builds/validation-attributes/models/Order.ts | 2 +- .../typescript-fetch/builds/with-interfaces/models/Order.ts | 2 +- .../builds/with-npm-version/src/models/Order.ts | 2 +- 16 files changed, 20 insertions(+), 20 deletions(-) diff --git a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/FormatTest.ts b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/FormatTest.ts index 33aed92595aa..f695e974f39d 100644 --- a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/FormatTest.ts +++ b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/FormatTest.ts @@ -178,8 +178,8 @@ export function FormatTestToJSONTyped(value?: FormatTest | null, ignoreDiscrimin 'string': value['string'], 'byte': value['_byte'], 'binary': value['binary'], - 'date': ((value['date']).toISOString().substring(0,10)), - 'dateTime': value['dateTime'] == null ? undefined : ((value['dateTime']).toISOString()), + 'date': value['date'].toISOString().substring(0,10), + 'dateTime': value['dateTime'] == null ? value['dateTime'] : value['dateTime'].toISOString(), 'uuid': value['uuid'], 'password': value['password'], 'pattern_with_digits': value['patternWithDigits'], diff --git a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/MixedPropertiesAndAdditionalPropertiesClass.ts b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/MixedPropertiesAndAdditionalPropertiesClass.ts index 5466bc5177e2..9263d67cc6f5 100644 --- a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/MixedPropertiesAndAdditionalPropertiesClass.ts +++ b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/MixedPropertiesAndAdditionalPropertiesClass.ts @@ -82,7 +82,7 @@ export function MixedPropertiesAndAdditionalPropertiesClassToJSONTyped(value?: M return { 'uuid': value['uuid'], - 'dateTime': value['dateTime'] == null ? undefined : ((value['dateTime']).toISOString()), + 'dateTime': value['dateTime'] == null ? value['dateTime'] : value['dateTime'].toISOString(), 'map': value['map'] == null ? undefined : (mapValues(value['map'], AnimalToJSON)), }; } diff --git a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/NullableClass.ts b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/NullableClass.ts index 425d7be4693d..11b2fb30dae8 100644 --- a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/NullableClass.ts +++ b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/NullableClass.ts @@ -143,8 +143,8 @@ export function NullableClassToJSONTyped(value?: NullableClass | null, ignoreDis 'number_prop': value['numberProp'], 'boolean_prop': value['booleanProp'], 'string_prop': value['stringProp'], - 'date_prop': value['dateProp'] === null ? null : ((value['dateProp'] as any)?.toISOString().substring(0,10)), - 'datetime_prop': value['datetimeProp'] === null ? null : ((value['datetimeProp'] as any)?.toISOString()), + 'date_prop': value['dateProp'] == null ? value['dateProp'] : value['dateProp'].toISOString().substring(0,10), + 'datetime_prop': value['datetimeProp'] == null ? value['datetimeProp'] : value['datetimeProp'].toISOString(), 'array_nullable_prop': value['arrayNullableProp'], 'array_and_items_nullable_prop': value['arrayAndItemsNullableProp'], 'array_items_nullable': value['arrayItemsNullable'], diff --git a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/Order.ts index 2e9a77af0484..9444d5346722 100644 --- a/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/default-v3.0/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/default/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/default/models/Order.ts index 46977acb9c08..42f520745eb0 100644 --- a/samples/client/petstore/typescript-fetch/builds/default/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/default/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/es6-target/src/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/es6-target/src/models/Order.ts index 46977acb9c08..42f520745eb0 100644 --- a/samples/client/petstore/typescript-fetch/builds/es6-target/src/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/es6-target/src/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/multiple-parameters/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/multiple-parameters/models/Order.ts index 46977acb9c08..42f520745eb0 100644 --- a/samples/client/petstore/typescript-fetch/builds/multiple-parameters/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/multiple-parameters/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/prefix-parameter-interfaces/src/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/prefix-parameter-interfaces/src/models/Order.ts index 46977acb9c08..42f520745eb0 100644 --- a/samples/client/petstore/typescript-fetch/builds/prefix-parameter-interfaces/src/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/prefix-parameter-interfaces/src/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/sagas-and-records/src/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/sagas-and-records/src/models/Order.ts index 46977acb9c08..42f520745eb0 100644 --- a/samples/client/petstore/typescript-fetch/builds/sagas-and-records/src/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/sagas-and-records/src/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/FormatTest.ts b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/FormatTest.ts index 33aed92595aa..f695e974f39d 100644 --- a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/FormatTest.ts +++ b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/FormatTest.ts @@ -178,8 +178,8 @@ export function FormatTestToJSONTyped(value?: FormatTest | null, ignoreDiscrimin 'string': value['string'], 'byte': value['_byte'], 'binary': value['binary'], - 'date': ((value['date']).toISOString().substring(0,10)), - 'dateTime': value['dateTime'] == null ? undefined : ((value['dateTime']).toISOString()), + 'date': value['date'].toISOString().substring(0,10), + 'dateTime': value['dateTime'] == null ? value['dateTime'] : value['dateTime'].toISOString(), 'uuid': value['uuid'], 'password': value['password'], 'pattern_with_digits': value['patternWithDigits'], diff --git a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/MixedPropertiesAndAdditionalPropertiesClass.ts b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/MixedPropertiesAndAdditionalPropertiesClass.ts index 5466bc5177e2..9263d67cc6f5 100644 --- a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/MixedPropertiesAndAdditionalPropertiesClass.ts +++ b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/MixedPropertiesAndAdditionalPropertiesClass.ts @@ -82,7 +82,7 @@ export function MixedPropertiesAndAdditionalPropertiesClassToJSONTyped(value?: M return { 'uuid': value['uuid'], - 'dateTime': value['dateTime'] == null ? undefined : ((value['dateTime']).toISOString()), + 'dateTime': value['dateTime'] == null ? value['dateTime'] : value['dateTime'].toISOString(), 'map': value['map'] == null ? undefined : (mapValues(value['map'], AnimalToJSON)), }; } diff --git a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/NullableClass.ts b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/NullableClass.ts index 425d7be4693d..11b2fb30dae8 100644 --- a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/NullableClass.ts +++ b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/NullableClass.ts @@ -143,8 +143,8 @@ export function NullableClassToJSONTyped(value?: NullableClass | null, ignoreDis 'number_prop': value['numberProp'], 'boolean_prop': value['booleanProp'], 'string_prop': value['stringProp'], - 'date_prop': value['dateProp'] === null ? null : ((value['dateProp'] as any)?.toISOString().substring(0,10)), - 'datetime_prop': value['datetimeProp'] === null ? null : ((value['datetimeProp'] as any)?.toISOString()), + 'date_prop': value['dateProp'] == null ? value['dateProp'] : value['dateProp'].toISOString().substring(0,10), + 'datetime_prop': value['datetimeProp'] == null ? value['datetimeProp'] : value['datetimeProp'].toISOString(), 'array_nullable_prop': value['arrayNullableProp'], 'array_and_items_nullable_prop': value['arrayAndItemsNullableProp'], 'array_items_nullable': value['arrayItemsNullable'], diff --git a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/Order.ts index 2e9a77af0484..9444d5346722 100644 --- a/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/snakecase-discriminator/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/validation-attributes/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/validation-attributes/models/Order.ts index 072d3bf5ac9e..097b74d85c79 100644 --- a/samples/client/petstore/typescript-fetch/builds/validation-attributes/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/validation-attributes/models/Order.ts @@ -112,7 +112,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/with-interfaces/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/with-interfaces/models/Order.ts index 46977acb9c08..42f520745eb0 100644 --- a/samples/client/petstore/typescript-fetch/builds/with-interfaces/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/with-interfaces/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], }; diff --git a/samples/client/petstore/typescript-fetch/builds/with-npm-version/src/models/Order.ts b/samples/client/petstore/typescript-fetch/builds/with-npm-version/src/models/Order.ts index 46977acb9c08..42f520745eb0 100644 --- a/samples/client/petstore/typescript-fetch/builds/with-npm-version/src/models/Order.ts +++ b/samples/client/petstore/typescript-fetch/builds/with-npm-version/src/models/Order.ts @@ -109,7 +109,7 @@ export function OrderToJSONTyped(value?: Order | null, ignoreDiscriminator: bool 'id': value['id'], 'petId': value['petId'], 'quantity': value['quantity'], - 'shipDate': value['shipDate'] == null ? undefined : ((value['shipDate']).toISOString()), + 'shipDate': value['shipDate'] == null ? value['shipDate'] : value['shipDate'].toISOString(), 'status': value['status'], 'complete': value['complete'], };