Skip to content

Commit fb6a300

Browse files
committed
fix(effect-schema): validate boolean strings
1 parent 7cc8d8a commit fb6a300

3 files changed

Lines changed: 17 additions & 2 deletions

File tree

packages/quicktype-core/src/language/TypeScriptEffectSchema/TypeScriptEffectSchemaRenderer.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,11 @@ export class TypeScriptEffectSchemaRenderer extends ConvenienceRenderer {
100100
return ["S.optional(", this.typeMapTypeFor(p.type), ")"];
101101
}
102102

103-
private typeMapTypeFor(t: Type, required = true): Sourcelike {
103+
private typeMapTypeFor(
104+
t: Type,
105+
required = true,
106+
coerceStrings = false,
107+
): Sourcelike {
104108
if (t.kind === "class" || t.kind === "object" || t.kind === "enum") {
105109
const name = this.nameForNamedType(t);
106110
if (this.emittedObjects.has(name)) {
@@ -143,6 +147,7 @@ export class TypeScriptEffectSchemaRenderer extends ConvenienceRenderer {
143147
(_enumType) => panic("Should already be handled."),
144148
(unionType) => {
145149
const types = Array.from(unionType.getChildren());
150+
const coerce = types.some((type) => type.kind === "bool");
146151
const rank = (type: Type): number =>
147152
type.kind === "class" || type.kind === "object" ? 1 : 0;
148153
types.sort((a, b) => rank(a) - rank(b));
@@ -152,7 +157,7 @@ export class TypeScriptEffectSchemaRenderer extends ConvenienceRenderer {
152157
if (type.kind === "null") {
153158
nullable = true;
154159
} else {
155-
children.push(this.typeMapTypeFor(type, false));
160+
children.push(this.typeMapTypeFor(type, false, coerce));
156161
}
157162
}
158163

@@ -167,6 +172,10 @@ export class TypeScriptEffectSchemaRenderer extends ConvenienceRenderer {
167172
];
168173
},
169174
(_transformedStringType) => {
175+
if (_transformedStringType.kind === "bool-string")
176+
return coerceStrings
177+
? 'S.transform(S.Literal("true", "false"), S.Boolean, { strict: true, decode: (value) => value === "true", encode: (value) => value ? "true" : "false" })'
178+
: 'S.Literal("true", "false")';
170179
return "S.String";
171180
},
172181
);

packages/quicktype-core/src/language/TypeScriptEffectSchema/language.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
type IntegerRange,
66
} from "../../support/IntegerRange.js";
77
import { TargetLanguage } from "../../TargetLanguage.js";
8+
import type { StringTypeMapping } from "../../Type/TypeBuilderUtils.js";
89
import type { LanguageName, RendererOptions } from "../../types.js";
910

1011
import { TypeScriptEffectSchemaRenderer } from "./TypeScriptEffectSchemaRenderer.js";
@@ -26,6 +27,10 @@ export class TypeScriptEffectSchemaTargetLanguage extends TargetLanguage<
2627
return JS_SAFE_INTEGER_RANGE;
2728
}
2829

30+
public get stringTypeMapping(): StringTypeMapping {
31+
return new Map([["bool-string", "bool-string"]]) as StringTypeMapping;
32+
}
33+
2934
public constructor() {
3035
super(typeScriptEffectSchemaLanguageConfig);
3136
}

test/languages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,7 @@ export const TypeScriptEffectSchemaLanguage: Language = {
16921692
"integer",
16931693
"strict-optional",
16941694
"minmaxitems",
1695+
"bool-string",
16951696
],
16961697
output: "TopLevel.ts",
16971698
topLevel: "TopLevel",

0 commit comments

Comments
 (0)