Skip to content

Commit a489265

Browse files
correctly narrow "number" type to "integer", fixes #1935 (#2192)
* Add failing test for integer subschema narrowing * Add number to includesType check for context types * narrow number to integer correctly * fix lint errors Co-authored-by: Jacob Ley <[email protected]>
1 parent a211e8d commit a489265

File tree

2 files changed

+121
-1
lines changed

2 files changed

+121
-1
lines changed

lib/compile/validate/index.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ function checkContextTypes(it: SchemaObjCxt, types: JSONType[]): void {
286286
strictTypesError(it, `type "${t}" not allowed by context "${it.dataTypes.join(",")}"`)
287287
}
288288
})
289-
it.dataTypes = it.dataTypes.filter((t) => includesType(types, t))
289+
narrowSchemaTypes(it, types)
290290
}
291291

292292
function checkMultipleTypes(it: SchemaObjCxt, ts: JSONType[]): void {
@@ -316,6 +316,15 @@ function includesType(ts: JSONType[], t: JSONType): boolean {
316316
return ts.includes(t) || (t === "integer" && ts.includes("number"))
317317
}
318318

319+
function narrowSchemaTypes(it: SchemaObjCxt, withTypes: JSONType[]): void {
320+
const ts: JSONType[] = []
321+
for (const t of it.dataTypes) {
322+
if (includesType(withTypes, t)) ts.push(t)
323+
else if (withTypes.includes("integer") && t === "number") ts.push("integer")
324+
}
325+
it.dataTypes = ts
326+
}
327+
319328
function strictTypesError(it: SchemaObjCxt, msg: string): void {
320329
const schemaPath = it.schemaEnv.baseId + it.errSchemaPath
321330
msg += ` at "${schemaPath}" (strictTypes)`
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import _Ajv from "../ajv"
2+
import Ajv from "ajv"
3+
import * as assert from "assert"
4+
5+
describe("integer valid type in number sub-schema (issue #1935)", () => {
6+
let ajv: Ajv
7+
before(() => {
8+
ajv = new _Ajv({strict: true})
9+
})
10+
11+
it("should allow integer in `if`", () =>
12+
assert.doesNotThrow(() =>
13+
ajv.compile({
14+
type: "number",
15+
if: {
16+
type: "integer",
17+
maximum: 5,
18+
},
19+
else: {
20+
minimum: 10,
21+
},
22+
})
23+
))
24+
25+
it("should allow integer in `then`", () =>
26+
assert.doesNotThrow(() =>
27+
ajv.compile({
28+
type: "number",
29+
if: {
30+
multipleOf: 2,
31+
},
32+
then: {
33+
type: "integer",
34+
minimum: 10,
35+
},
36+
})
37+
))
38+
39+
it("should allow integer in `else`", () =>
40+
assert.doesNotThrow(() =>
41+
ajv.compile({
42+
type: "number",
43+
if: {
44+
maximum: 5,
45+
},
46+
else: {
47+
type: "integer",
48+
minimum: 10,
49+
},
50+
})
51+
))
52+
53+
it("should allow integer in `allOf`", () =>
54+
assert.doesNotThrow(() =>
55+
ajv.compile({
56+
type: "number",
57+
allOf: [
58+
{
59+
type: "integer",
60+
minimum: 10,
61+
},
62+
{
63+
multipleOf: 2,
64+
},
65+
],
66+
})
67+
))
68+
69+
it("should allow integer in `oneOf`", () =>
70+
assert.doesNotThrow(() =>
71+
ajv.compile({
72+
type: "number",
73+
oneOf: [
74+
{
75+
type: "integer",
76+
minimum: 10,
77+
},
78+
{
79+
multipleOf: 2,
80+
},
81+
],
82+
})
83+
))
84+
85+
it("should allow integer in `anyOf`", () =>
86+
assert.doesNotThrow(() =>
87+
ajv.compile({
88+
type: "number",
89+
oneOf: [
90+
{
91+
type: "integer",
92+
minimum: 10,
93+
},
94+
{
95+
multipleOf: 2,
96+
},
97+
],
98+
})
99+
))
100+
101+
it("should allow integer in `not`", () =>
102+
assert.doesNotThrow(() =>
103+
ajv.compile({
104+
type: "number",
105+
not: {
106+
type: "integer",
107+
minimum: 10,
108+
},
109+
})
110+
))
111+
})

0 commit comments

Comments
 (0)