Skip to content

Commit d5b613f

Browse files
Combine type error messages into a single meaningful error (#147)
* Refactor type error handler to combine type error messages * Add test for type error handler * change getUnknownErrorMessage to getBooleanSchemaErrorMessage and add test
1 parent eb5d98a commit d5b613f

2 files changed

Lines changed: 77 additions & 12 deletions

File tree

src/error-handlers/type.js

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,53 @@ import * as Instance from "@hyperjump/json-schema/instance/experimental";
66
* @import { ErrorHandler, ErrorObject } from "../index.d.ts"
77
*/
88

9+
const ALL_TYPES = new Set(["null", "boolean", "number", "string", "array", "object", "integer"]);
10+
911
/** @type ErrorHandler */
1012
const typeErrorHandler = async (normalizedErrors, instance, localization) => {
1113
/** @type ErrorObject[] */
1214
const errors = [];
1315

14-
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/type"]) {
15-
if (normalizedErrors["https://json-schema.org/keyword/type"][schemaLocation]) {
16-
continue;
16+
if (normalizedErrors["https://json-schema.org/keyword/type"]) {
17+
/** @type {Set<string>} */
18+
let allowedTypes = ALL_TYPES;
19+
const failedTypeLocations = [];
20+
21+
for (const schemaLocation in normalizedErrors["https://json-schema.org/keyword/type"]) {
22+
const isValid = normalizedErrors["https://json-schema.org/keyword/type"][schemaLocation];
23+
if (!isValid) {
24+
failedTypeLocations.push(schemaLocation);
25+
26+
const keyword = await getSchema(schemaLocation);
27+
/** @type {string|string[]} */
28+
const value = Schema.value(keyword);
29+
const types = Array.isArray(value) ? value : [value];
30+
/** @type {Set<string>} */
31+
const keywordTypes = new Set(types);
32+
if (keywordTypes.has("number")) {
33+
keywordTypes.add("integer");
34+
}
35+
allowedTypes = allowedTypes.intersection(keywordTypes);
36+
}
1737
}
1838

19-
const keyword = await getSchema(schemaLocation);
20-
const expectedTypes = /** @type string[] */ (Schema.typeOf(keyword) === "array"
21-
? Schema.value(keyword)
22-
: [Schema.value(keyword)]);
39+
if (allowedTypes.has("number")) {
40+
allowedTypes.delete("integer");
41+
}
2342

24-
errors.push({
25-
message: localization.getTypeErrorMessage(expectedTypes),
26-
instanceLocation: Instance.uri(instance),
27-
schemaLocations: [schemaLocation]
28-
});
43+
if (allowedTypes.size === 0) {
44+
errors.push({
45+
message: localization.getBooleanSchemaErrorMessage(),
46+
instanceLocation: Instance.uri(instance),
47+
schemaLocations: failedTypeLocations
48+
});
49+
} else if (failedTypeLocations.length > 0) {
50+
errors.push({
51+
message: localization.getTypeErrorMessage([...allowedTypes]),
52+
instanceLocation: Instance.uri(instance),
53+
schemaLocations: failedTypeLocations
54+
});
55+
}
2956
}
3057

3158
return errors;

src/test-suite/tests/type.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,44 @@
3636
"schemaLocations": ["#/type"]
3737
}
3838
]
39+
},
40+
{
41+
"description": "collapsed type errors",
42+
"schema": {
43+
"allOf": [
44+
{ "type": ["string", "boolean", "number"] },
45+
{ "type": ["string", "number"] },
46+
{ "type": "string" }
47+
]
48+
},
49+
"instance": null,
50+
"errors": [
51+
{
52+
"messageId": "type-message",
53+
"messageParams": {
54+
"expectedTypes": { "or": ["string"] }
55+
},
56+
"instanceLocation": "#",
57+
"schemaLocations": ["#/allOf/0/type", "#/allOf/1/type", "#/allOf/2/type"]
58+
}
59+
]
60+
},
61+
{
62+
"description": "no types allowed",
63+
"schema": {
64+
"allOf": [
65+
{ "type": "string" },
66+
{ "type": "number" }
67+
]
68+
},
69+
"instance": null,
70+
"errors": [
71+
{
72+
"messageId": "boolean-schema-message",
73+
"instanceLocation": "#",
74+
"schemaLocations": ["#/allOf/0/type", "#/allOf/1/type"]
75+
}
76+
]
3977
}
4078
]
4179
}

0 commit comments

Comments
 (0)