Skip to content

Commit ac3a897

Browse files
authored
Merge pull request #85 from mlewando/fix--getSchemaType-typings
2 parents c3a86d6 + a6daf1c commit ac3a897

File tree

9 files changed

+142
-28
lines changed

9 files changed

+142
-28
lines changed

dist/index.d.mts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,13 +636,14 @@ declare function mergeNode(a: SchemaNode, b?: SchemaNode, ...omit: string[]): Sc
636636
declare function mergeSchema<T extends JsonSchema>(a: T, b: T, ...omit: string[]): T;
637637
//#endregion
638638
//#region src/utils/getSchemaType.d.ts
639-
declare const SCHEMA_TYPES: string[];
639+
declare const SCHEMA_TYPES: readonly ["string", "number", "integer", "boolean", "null", "array", "object"];
640+
type SchemaType = (typeof SCHEMA_TYPES)[number];
640641
/**
641642
* @helper for getData
642643
* returns schema type, which might be an educated guess based on defined schema
643644
* properties if an exact type cannot be retried from type.
644645
*/
645-
declare function getSchemaType(node: SchemaNode, data: unknown): keyof typeof SCHEMA_TYPES | undefined;
646+
declare function getSchemaType(node: SchemaNode, data: unknown): SchemaType | undefined;
646647
//#endregion
647648
//#region remotes/index.d.ts
648649
/** remote meta-schema stored by schema $id */

dist/index.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,13 +636,14 @@ declare function mergeNode(a: SchemaNode, b?: SchemaNode, ...omit: string[]): Sc
636636
declare function mergeSchema<T extends JsonSchema>(a: T, b: T, ...omit: string[]): T;
637637
//#endregion
638638
//#region src/utils/getSchemaType.d.ts
639-
declare const SCHEMA_TYPES: string[];
639+
declare const SCHEMA_TYPES: readonly ["string", "number", "integer", "boolean", "null", "array", "object"];
640+
type SchemaType = (typeof SCHEMA_TYPES)[number];
640641
/**
641642
* @helper for getData
642643
* returns schema type, which might be an educated guess based on defined schema
643644
* properties if an exact type cannot be retried from type.
644645
*/
645-
declare function getSchemaType(node: SchemaNode, data: unknown): keyof typeof SCHEMA_TYPES | undefined;
646+
declare function getSchemaType(node: SchemaNode, data: unknown): SchemaType | undefined;
646647
//#endregion
647648
//#region remotes/index.d.ts
648649
/** remote meta-schema stored by schema $id */

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.mjs

Lines changed: 1 addition & 1 deletion
Large diffs are not rendered by default.

dist/jlib.js

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/draft2019-09/keywords/$ref.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function parseRef(node: SchemaNode) {
7272
export function resolveRef({ pointer, path }: { pointer?: string; path?: ValidationPath } = {}) {
7373
const node = this as SchemaNode;
7474
if (node.schema.$recursiveRef) {
75-
const nextNode = resolveRecursiveRef(node, path);
75+
const nextNode = resolveRecursiveRef(node, path ?? []);
7676
path?.push({ pointer, node: nextNode });
7777
return nextNode;
7878
}

src/draft2019-09/methods/getData.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import copy from "fast-copy";
22
import { getTypeOf } from "../../utils/getTypeOf";
3-
import { getSchemaType } from "../../utils/getSchemaType";
3+
import { getSchemaType, SchemaType } from "../../utils/getSchemaType";
44
import { getValue } from "../../utils/getValue";
55
import { isEmpty } from "../../utils/isEmpty";
66
import { isJsonError } from "../../types";
@@ -172,11 +172,11 @@ export function getData(node: SchemaNode, data?: unknown, opts?: TemplateOptions
172172
// }
173173

174174
const type = getSchemaType(currentNode, defaultData);
175-
const templateData = TYPE[type as string]?.(currentNode, defaultData, opts);
175+
const templateData = TYPE[type]?.(currentNode, defaultData, opts);
176176
return templateData === undefined ? defaultData : templateData;
177177
}
178178

179-
const TYPE: Record<string, (node: SchemaNode, data: unknown, opts: TemplateOptions) => unknown> = {
179+
const TYPE: Record<SchemaType, (node: SchemaNode, data: unknown, opts: TemplateOptions) => unknown> = {
180180
null: (node, data, opts) => getDefault(node, data, null, opts.useTypeDefaults),
181181
string: (node, data, opts) => getDefault(node, data, "", opts.useTypeDefaults),
182182
number: (node, data, opts) => getDefault(node, data, 0, opts.useTypeDefaults),

src/utils/getSchemaType.test.ts

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import { compileSchema } from "../compileSchema";
2+
import { getSchemaType } from "./getSchemaType";
3+
import { strict as assert } from "assert";
4+
5+
describe("issue#90 - types or refs", () => {
6+
it("should get correct type for simple ref", () => {
7+
const schema = compileSchema({
8+
type: "object",
9+
$defs: {
10+
customConst: {
11+
type: "string"
12+
}
13+
},
14+
properties: {
15+
name: { $ref: "#/$defs/customConst" }
16+
}
17+
});
18+
const nameProp = schema.getNodeChild("name").node;
19+
assert(nameProp != null);
20+
assert(getSchemaType(nameProp, undefined) === "string");
21+
});
22+
23+
it("should handle oneOf refs", () => {
24+
const schema = compileSchema({
25+
type: "object",
26+
$defs: {
27+
a: {
28+
type: "string",
29+
const: "a"
30+
},
31+
b: {
32+
type: "string",
33+
const: "b"
34+
}
35+
},
36+
37+
properties: {
38+
oneOf: {
39+
oneOf: [{ $ref: "#/$defs/a" }, { $ref: "#/$defs/b" }]
40+
}
41+
}
42+
});
43+
const oneOfProp = schema.getNodeChild("oneOf").node;
44+
assert(oneOfProp != null);
45+
assert(getSchemaType(oneOfProp, undefined) === "string");
46+
});
47+
it("should handle anyOf refs", () => {
48+
const schema = compileSchema({
49+
type: "object",
50+
$defs: {
51+
a: { type: "string", const: "a" },
52+
b: { type: "string", const: "b" }
53+
},
54+
properties: {
55+
anyOf: {
56+
anyOf: [{ $ref: "#/$defs/a" }, { $ref: "#/$defs/b" }]
57+
}
58+
}
59+
});
60+
const anyOfProp = schema.getNodeChild("anyOf").node;
61+
assert(anyOfProp != null);
62+
assert(getSchemaType(anyOfProp, undefined) === "string");
63+
});
64+
it("should handle allOf refs", () => {
65+
const schema = compileSchema({
66+
type: "object",
67+
$defs: {
68+
a: { type: "object", properties: { a: { type: "string" } } },
69+
b: { type: "object", properties: { b: { type: "string" } } }
70+
},
71+
properties: {
72+
allOf: {
73+
allOf: [{ $ref: "#/$defs/a" }, { $ref: "#/$defs/b" }]
74+
}
75+
}
76+
});
77+
const allOfProp = schema.getNodeChild("allOf").node;
78+
assert(allOfProp != null);
79+
assert(getSchemaType(allOfProp, undefined) === "object");
80+
});
81+
it("should handle if/then/else refs", () => {
82+
const schema = compileSchema({
83+
type: "object",
84+
$defs: {
85+
stringSchema: {
86+
type: "string"
87+
}
88+
},
89+
properties: {
90+
conditional: {
91+
if: { $ref: "#/$defs/stringSchema" },
92+
then: { minLength: 5 },
93+
else: { maxLength: 2 }
94+
}
95+
}
96+
});
97+
const conditionalProp = schema.getNodeChild("conditional").node;
98+
assert(conditionalProp != null);
99+
assert(getSchemaType(conditionalProp, undefined) === "string");
100+
});
101+
});

src/utils/getSchemaType.ts

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { getTypeOf } from "./getTypeOf";
22
import { isObject } from "../utils/isObject";
33
import { BooleanSchema, JsonSchema, SchemaNode } from "../types";
44

5-
export const SCHEMA_TYPES = ["string", "number", "integer", "boolean", "null", "array", "object"];
5+
export const SCHEMA_TYPES = ["string", "number", "integer", "boolean", "null", "array", "object"] as const;
6+
export type SchemaType = (typeof SCHEMA_TYPES)[number];
67
const OBJECT_PROPERTIES = [
78
"additionalProperties",
89
// "allOf",
@@ -43,11 +44,14 @@ const ARRAY_PROPERTIES = [
4344
* returns schema type, which might be an educated guess based on defined schema
4445
* properties if an exact type cannot be retried from type.
4546
*/
46-
export function getSchemaType(node: SchemaNode, data: unknown): keyof typeof SCHEMA_TYPES | undefined {
47+
export function getSchemaType(node: SchemaNode, data: unknown): SchemaType | undefined {
4748
const dataType = getTypeOf(data);
4849
const schema = node.schema as JsonSchema | BooleanSchema;
4950
if (schema === true) {
50-
return SCHEMA_TYPES.includes(dataType) ? (dataType as keyof typeof SCHEMA_TYPES) : undefined;
51+
if (dataType === "bigint") {
52+
return "number";
53+
}
54+
return SCHEMA_TYPES.some((schemaType) => schemaType === dataType) ? (dataType as SchemaType) : undefined;
5155
}
5256
// boolean schema false or invalid schema
5357
if (!isObject(schema)) {
@@ -58,32 +62,32 @@ export function getSchemaType(node: SchemaNode, data: unknown): keyof typeof SCH
5862
// type: []
5963
if (Array.isArray(schemaType)) {
6064
if (schemaType.includes(dataType)) {
61-
return dataType as keyof typeof SCHEMA_TYPES;
65+
return dataType as SchemaType;
6266
}
6367
const defaultType = getTypeOf(schema.default);
6468
if (schemaType.includes(defaultType)) {
65-
return defaultType as keyof typeof SCHEMA_TYPES;
69+
return defaultType as SchemaType;
6670
}
6771
return schemaType[0];
6872
}
6973

7074
// type: ""
7175
if (schemaType) {
72-
return schemaType as keyof typeof SCHEMA_TYPES;
76+
return schemaType as SchemaType;
7377
}
7478

7579
// type: undefined, enum: []
7680
if (Array.isArray(schema.enum)) {
7781
const schemaEnum: unknown[] = schema.enum;
7882
const enumSchemaType = schemaEnum.map((value) => getTypeOf(value)).filter((p, i, l) => l.indexOf(p) === i);
7983
if (enumSchemaType.includes(dataType)) {
80-
return dataType as keyof typeof SCHEMA_TYPES;
84+
return dataType as SchemaType;
8185
}
8286
const defaultType = getTypeOf(schema.default);
8387
if (enumSchemaType.includes(defaultType)) {
84-
return defaultType as keyof typeof SCHEMA_TYPES;
88+
return defaultType as SchemaType;
8589
}
86-
return enumSchemaType[0] as keyof typeof SCHEMA_TYPES;
90+
return enumSchemaType[0] as SchemaType;
8791
}
8892

8993
// type: undefined, enum: undefined -- define type by schema-properties
@@ -93,21 +97,21 @@ export function getSchemaType(node: SchemaNode, data: unknown): keyof typeof SCH
9397
const arrayProperties = schemaProperties.filter((p) => ARRAY_PROPERTIES.includes(p));
9498

9599
if (objectProperties.length > 0 && objectProperties.length > arrayProperties.length) {
96-
return "object" as keyof typeof SCHEMA_TYPES;
100+
return "object";
97101
}
98102

99103
if (arrayProperties.length > 0 && arrayProperties.length > objectProperties.length) {
100-
return "array" as keyof typeof SCHEMA_TYPES;
104+
return "array";
101105
}
102106

103107
// nothing found yet check dynamic properties for a type
104108
if (node.if) {
105-
return getSchemaType(node.if, data);
109+
return getSchemaType(node.if.resolveRef?.() ?? node.if, data);
106110
}
107111

108112
if (node.allOf) {
109113
for (let i = 0; i < node.allOf.length; i += 1) {
110-
const type = getSchemaType(node.allOf[i], data);
114+
const type = getSchemaType(node.allOf[i].resolveRef?.() ?? node.allOf[i], data);
111115
if (type) {
112116
return type;
113117
}
@@ -116,7 +120,7 @@ export function getSchemaType(node: SchemaNode, data: unknown): keyof typeof SCH
116120

117121
if (node.oneOf) {
118122
for (let i = 0; i < node.oneOf.length; i += 1) {
119-
const type = getSchemaType(node.oneOf[i], data);
123+
const type = getSchemaType(node.oneOf[i].resolveRef?.() ?? node.oneOf[i], data);
120124
if (type) {
121125
return type;
122126
}
@@ -125,12 +129,19 @@ export function getSchemaType(node: SchemaNode, data: unknown): keyof typeof SCH
125129

126130
if (node.anyOf) {
127131
for (let i = 0; i < node.anyOf.length; i += 1) {
128-
const type = getSchemaType(node.anyOf[i], data);
132+
const type = getSchemaType(node.anyOf[i].resolveRef?.() ?? node.anyOf[i], data);
129133
if (type) {
130134
return type;
131135
}
132136
}
133137
}
134138

139+
if (schema.$ref) {
140+
const refNode = node.resolveRef?.();
141+
if (refNode) {
142+
return getSchemaType(refNode, data);
143+
}
144+
}
145+
135146
return undefined;
136147
}

0 commit comments

Comments
 (0)