Skip to content

Commit 064751e

Browse files
maxholmanclaude
andcommitted
fix: handle OAS 3.1 type arrays and const schema values
type: ["string", "null"] (valid in OAS 3.1) fell through to v.unknown() because neither the type nor valibot codegen handled array-form types. const schemas were ignored in valibot output entirely, and only handled for strings in type generation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 4c2daf5 commit 064751e

2 files changed

Lines changed: 81 additions & 20 deletions

File tree

lib/process-schema.ts

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,50 @@ export function registerTypesFromSchema(
568568
typesAndInterfaces.set(`#/components/schemas/${schemaName}`, typeAlias);
569569
}
570570

571+
// deal with type arrays (OpenAPI 3.1: type: ["string", "null"])
572+
else if (Array.isArray(schemaObject.type)) {
573+
const prop = schemaToType(
574+
typesAndInterfaces,
575+
{},
576+
schemaName,
577+
schemaObject,
578+
);
579+
580+
const typeAlias = typesFile.addTypeAlias({
581+
name: pascalCase(schemaName),
582+
isExported: true,
583+
type: prop.type || "unknown",
584+
});
585+
586+
if (schemaObject.description) {
587+
typeAlias.addJsDoc({
588+
description: wordWrap(schemaObject.description),
589+
});
590+
}
591+
592+
typesAndInterfaces.set(`#/components/schemas/${schemaName}`, typeAlias);
593+
}
594+
595+
// deal with const values
596+
else if ("const" in schemaObject) {
597+
const constDeclaration = typesFile.addTypeAlias({
598+
isExported: true,
599+
name: pascalCase(schemaName),
600+
type: JSON.stringify(schemaObject.const),
601+
});
602+
603+
if (schemaObject.description) {
604+
constDeclaration.addJsDoc({
605+
description: wordWrap(schemaObject.description),
606+
});
607+
}
608+
609+
typesAndInterfaces.set(
610+
`#/components/schemas/${schemaName}`,
611+
constDeclaration,
612+
);
613+
}
614+
571615
// deal with objects
572616
else if (!schemaObject.type || schemaObject.type === "object") {
573617
const newIf = typesFile.addTypeAlias({
@@ -643,26 +687,6 @@ export function registerTypesFromSchema(
643687
// );
644688
}
645689

646-
// deal with string consts
647-
else if (schemaObject.type === "string" && "const" in schemaObject) {
648-
const constDeclaration = typesFile.addTypeAlias({
649-
isExported: true,
650-
name: pascalCase(schemaName),
651-
type: JSON.stringify(schemaObject.const),
652-
});
653-
654-
if (schemaObject.description) {
655-
constDeclaration.addJsDoc({
656-
description: wordWrap(schemaObject.description),
657-
});
658-
}
659-
660-
typesAndInterfaces.set(
661-
`#/components/schemas/${schemaName}`,
662-
constDeclaration,
663-
);
664-
}
665-
666690
// deal with non-enum strings
667691
else if (schemaObject.type === "string" && !schemaObject.enum) {
668692
const typeAlias = typesFile.addTypeAlias({

lib/valibot.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,43 @@ export function schemaToValidator(
9393
? `v.custom<${typescriptHint}>(() => true)`
9494
: undefined;
9595

96+
// Handle const values (OpenAPI 3.1: const: "value")
97+
if ("const" in schema) {
98+
return schema.const === null
99+
? vcall("null")
100+
: maybeNullable(
101+
vcall("literal", JSON.stringify(schema.const)),
102+
isNullable,
103+
);
104+
}
105+
106+
// Handle type arrays (OpenAPI 3.1: type: ["string", "null"])
107+
if (Array.isArray(schema.type)) {
108+
const nonNullTypes = schema.type.filter((t) => t !== "null");
109+
const [singleType] = nonNullTypes;
110+
111+
if (nonNullTypes.length === 1 && singleType) {
112+
return maybeNullable(
113+
schemaToValidator(validators, {
114+
...schema,
115+
type: singleType,
116+
} satisfies typeof schema),
117+
isNullable,
118+
);
119+
}
120+
121+
const variants = nonNullTypes.map((t) =>
122+
schemaToValidator(validators, {
123+
...schema,
124+
type: t,
125+
} satisfies typeof schema),
126+
);
127+
return maybeNullable(
128+
variants.length > 0 ? vcall("union", variants) : vcall("unknown"),
129+
isNullable,
130+
);
131+
}
132+
96133
if (schema.type === "string") {
97134
if (schema.enum) {
98135
return maybeNullable(

0 commit comments

Comments
 (0)