Skip to content

Commit d7c7c7a

Browse files
committed
fix(core): handle type arrays (e.g. ["array","null"]) in form-urlencoded serialization
After @scalar/openapi-upgrader converts OpenAPI 3.0 nullable arrays to 3.1 format, property.type becomes ["array", "null"] instead of "array". The strict === checks in resolveSchemaPropertiesToFormData fell through to the generic else branch, generating a bare formUrlEncoded.append('tags', value) instead of a forEach loop, causing a TS2345 compile error. Fixes #3418
1 parent cb9698d commit d7c7c7a

2 files changed

Lines changed: 61 additions & 2 deletions

File tree

packages/core/src/getters/res-req-types.test.ts

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -506,6 +506,59 @@ bodyRequestBody.photos.forEach(value => formData.append(\`photos\`, value));
506506
expect(result.formUrlEncoded).not.toContain('Blob');
507507
});
508508

509+
it('generates forEach for nullable array property (type: ["array","null"] — post-3.1 upgrade)', () => {
510+
// After @scalar/openapi-upgrader converts a 3.0 spec, nullable arrays
511+
// become `type: ["array", "null"]`. The type check must handle arrays.
512+
const reqBodyNullableArray: [string, OpenApiRequestBodyObject][] = [
513+
[
514+
'requestBody',
515+
{
516+
content: {
517+
'application/x-www-form-urlencoded': {
518+
schema: {
519+
type: 'object',
520+
required: ['petId'],
521+
properties: {
522+
petId: { type: 'string' },
523+
tags: {
524+
type: ['array', 'null'] as unknown as 'array',
525+
nullable: true,
526+
items: {
527+
type: 'object',
528+
required: ['tagId', 'label'],
529+
properties: {
530+
tagId: { type: 'string' },
531+
label: { type: 'string' },
532+
},
533+
},
534+
},
535+
},
536+
},
537+
},
538+
},
539+
required: true,
540+
},
541+
],
542+
];
543+
544+
const result = getResReqTypes(
545+
reqBodyNullableArray,
546+
'UpdatePet',
547+
context,
548+
)[0];
549+
550+
const formUrlEncoded = result.formUrlEncoded;
551+
if (!formUrlEncoded || !isString(formUrlEncoded)) {
552+
throw new Error('Expected formUrlEncoded to be a defined string');
553+
}
554+
555+
// Must generate a forEach loop, not a bare append of the whole array
556+
expect(formUrlEncoded).toContain('forEach');
557+
expect(formUrlEncoded).not.toMatch(
558+
/append\(`tags`,\s*updatePetRequestBody\.tags\)/,
559+
);
560+
});
561+
509562
it('uses a string-only runtime loop for oneOf/anyOf url-encoded bodies', () => {
510563
const oneOfReqBody: [string, OpenApiRequestBodyObject][] = [
511564
[

packages/core/src/getters/res-req-types.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -773,7 +773,10 @@ function resolveSchemaPropertiesToFormData({
773773
} else if (fileType === 'text') {
774774
// Text file: value is Blob | string, check at runtime
775775
formDataValue = `${variableName}.append(\`${keyPrefix}${key}\`, ${nonOptionalValueKey} instanceof Blob ? ${nonOptionalValueKey} : new Blob([${nonOptionalValueKey}], { type: '${effectiveContentType}' }));\n`;
776-
} else if (property.type === 'object') {
776+
} else if (
777+
property.type === 'object' ||
778+
(Array.isArray(property.type) && property.type.includes('object'))
779+
) {
777780
formDataValue =
778781
context.output.override.formData.arrayHandling ===
779782
FormDataArrayHandling.EXPLODE
@@ -788,7 +791,10 @@ function resolveSchemaPropertiesToFormData({
788791
encoding,
789792
})
790793
: `${variableName}.append(\`${keyPrefix}${key}\`, JSON.stringify(${nonOptionalValueKey}));\n`;
791-
} else if (property.type === 'array') {
794+
} else if (
795+
property.type === 'array' ||
796+
(Array.isArray(property.type) && property.type.includes('array'))
797+
) {
792798
let valueStr = 'value';
793799
let hasNonPrimitiveChild = false;
794800
const propertyItems = getSchemaItems(property);

0 commit comments

Comments
 (0)