@oh-my-pi/omptype 17.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +60 -0
- package/dist/types/ark.d.ts +26 -0
- package/dist/types/compile.d.ts +5 -0
- package/dist/types/errors.d.ts +63 -0
- package/dist/types/index.d.ts +13 -0
- package/dist/types/infer.d.ts +49 -0
- package/dist/types/interp.d.ts +24 -0
- package/dist/types/ir.d.ts +115 -0
- package/dist/types/json-schema.d.ts +8 -0
- package/dist/types/type.d.ts +100 -0
- package/dist/types/typebox.d.ts +184 -0
- package/dist/types/zod.d.ts +100 -0
- package/package.json +56 -0
- package/src/ark.ts +30 -0
- package/src/compile.ts +415 -0
- package/src/errors.ts +136 -0
- package/src/index.ts +13 -0
- package/src/infer.ts +160 -0
- package/src/interp.ts +282 -0
- package/src/ir.ts +480 -0
- package/src/json-schema.ts +172 -0
- package/src/type.ts +329 -0
- package/src/typebox.ts +487 -0
- package/src/zod.ts +339 -0
package/src/infer.ts
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/** Type-level output inference for the definition forms accepted by omptype. */
|
|
2
|
+
|
|
3
|
+
type Whitespace = " " | "\n" | "\r" | "\t";
|
|
4
|
+
|
|
5
|
+
type TrimLeft<s extends string> = s extends `${Whitespace}${infer rest}` ? TrimLeft<rest> : s;
|
|
6
|
+
type TrimRight<s extends string> = s extends `${infer rest}${Whitespace}` ? TrimRight<rest> : s;
|
|
7
|
+
type Trim<s extends string> = TrimLeft<TrimRight<s>>;
|
|
8
|
+
|
|
9
|
+
type InferPrimitive<s extends string> = s extends "string" | "string.url"
|
|
10
|
+
? string
|
|
11
|
+
: s extends "number" | "number.integer"
|
|
12
|
+
? number
|
|
13
|
+
: s extends "boolean"
|
|
14
|
+
? boolean
|
|
15
|
+
: s extends "null"
|
|
16
|
+
? null
|
|
17
|
+
: s extends "undefined"
|
|
18
|
+
? undefined
|
|
19
|
+
: s extends "unknown"
|
|
20
|
+
? unknown
|
|
21
|
+
: s extends "object"
|
|
22
|
+
? object
|
|
23
|
+
: s extends "bigint"
|
|
24
|
+
? bigint
|
|
25
|
+
: s extends "true"
|
|
26
|
+
? true
|
|
27
|
+
: s extends "false"
|
|
28
|
+
? false
|
|
29
|
+
: never;
|
|
30
|
+
|
|
31
|
+
type InferMember<member extends string> =
|
|
32
|
+
Trim<member> extends infer s extends string
|
|
33
|
+
? s extends `${infer element}[]`
|
|
34
|
+
? InferMember<element>[]
|
|
35
|
+
: s extends `'${infer literal}'` | `"${infer literal}"`
|
|
36
|
+
? literal
|
|
37
|
+
: s extends `${infer literal extends number}`
|
|
38
|
+
? literal
|
|
39
|
+
: InferPrimitive<s> extends infer primitive
|
|
40
|
+
? [primitive] extends [never]
|
|
41
|
+
? s extends `${string}string${string}`
|
|
42
|
+
? string
|
|
43
|
+
: s extends `${string}number${string}`
|
|
44
|
+
? number
|
|
45
|
+
: unknown
|
|
46
|
+
: primitive
|
|
47
|
+
: unknown
|
|
48
|
+
: unknown;
|
|
49
|
+
|
|
50
|
+
/** Split top-level unions without distributing over the accumulated members. */
|
|
51
|
+
type InferUnion<s extends string, result = never> = s extends `${infer head}|${infer tail}`
|
|
52
|
+
? InferUnion<tail, result | InferMember<head>>
|
|
53
|
+
: result | InferMember<s>;
|
|
54
|
+
|
|
55
|
+
type HasInlineDefault<s extends string> = s extends `${string}=${string}`
|
|
56
|
+
? s extends `${string}<${string}` | `${string}>${string}`
|
|
57
|
+
? false
|
|
58
|
+
: true
|
|
59
|
+
: false;
|
|
60
|
+
|
|
61
|
+
type WithoutInlineDefault<s extends string> =
|
|
62
|
+
HasInlineDefault<s> extends true ? (s extends `${infer base}=${string}` ? Trim<base> : s) : s;
|
|
63
|
+
|
|
64
|
+
/** String-DSL-only inference; safe inside recursive interfaces (never probes `Type`). */
|
|
65
|
+
export type InferString<s extends string> =
|
|
66
|
+
WithoutInlineDefault<Trim<s>> extends infer trimmed extends string
|
|
67
|
+
? trimmed extends `${infer base}?`
|
|
68
|
+
? InferString<base>
|
|
69
|
+
: trimmed extends `(${infer inner})[]`
|
|
70
|
+
? InferUnion<inner>[]
|
|
71
|
+
: InferUnion<trimmed>
|
|
72
|
+
: unknown;
|
|
73
|
+
|
|
74
|
+
type HasDefault<def> = def extends string
|
|
75
|
+
? HasInlineDefault<def>
|
|
76
|
+
: def extends { readonly hasDefault: true }
|
|
77
|
+
? true
|
|
78
|
+
: false;
|
|
79
|
+
|
|
80
|
+
type DefinitionKeys<def extends object> = Exclude<keyof def, "+" | "[string]">;
|
|
81
|
+
|
|
82
|
+
/** Optional when the key carries a `?` suffix or the string value does (`limit: "number?"`). */
|
|
83
|
+
type IsOptionalProp<key, def> = key extends `${string}?`
|
|
84
|
+
? true
|
|
85
|
+
: def extends string
|
|
86
|
+
? Trim<def> extends `${string}?`
|
|
87
|
+
? true
|
|
88
|
+
: false
|
|
89
|
+
: false;
|
|
90
|
+
|
|
91
|
+
type PropName<key extends PropertyKey> = key extends `${infer name}?` ? name : key;
|
|
92
|
+
|
|
93
|
+
type RequiredProperties<def extends object> = {
|
|
94
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
|
|
95
|
+
? HasDefault<def[key]> extends true
|
|
96
|
+
? PropName<key>
|
|
97
|
+
: never
|
|
98
|
+
: PropName<key>]-?: InferDef<def[key]>;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
type OptionalProperties<def extends object> = {
|
|
102
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
|
|
103
|
+
? HasDefault<def[key]> extends true
|
|
104
|
+
? never
|
|
105
|
+
: PropName<key>
|
|
106
|
+
: never]?: InferDef<def[key]>;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
type Simplify<t> = { [key in keyof t]: t[key] };
|
|
110
|
+
type PropertiesOf<def extends object> = Simplify<RequiredProperties<def> & OptionalProperties<def>>;
|
|
111
|
+
|
|
112
|
+
type InferObject<def extends object> = "[string]" extends keyof def
|
|
113
|
+
? [DefinitionKeys<def>] extends [never]
|
|
114
|
+
? Record<string, InferDef<def["[string]"]>>
|
|
115
|
+
: PropertiesOf<def> & Record<string, InferDef<def["[string]"]>>
|
|
116
|
+
: PropertiesOf<def>;
|
|
117
|
+
|
|
118
|
+
/** Object-literal inference for fluent composition without re-inspecting the containing `Type`. */
|
|
119
|
+
export type InferObjectDef<def extends object> = InferObject<def>;
|
|
120
|
+
|
|
121
|
+
type InferLiteralDef<def> = def extends string
|
|
122
|
+
? InferString<def>
|
|
123
|
+
: def extends object
|
|
124
|
+
? InferObjectLiteral<def>
|
|
125
|
+
: unknown;
|
|
126
|
+
|
|
127
|
+
type LiteralRequiredProperties<def extends object> = {
|
|
128
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
|
|
129
|
+
? HasDefault<def[key]> extends true
|
|
130
|
+
? PropName<key>
|
|
131
|
+
: never
|
|
132
|
+
: PropName<key>]-?: InferLiteralDef<def[key]>;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
type LiteralOptionalProperties<def extends object> = {
|
|
136
|
+
-readonly [key in DefinitionKeys<def> as IsOptionalProp<key, def[key]> extends true
|
|
137
|
+
? HasDefault<def[key]> extends true
|
|
138
|
+
? never
|
|
139
|
+
: PropName<key>
|
|
140
|
+
: never]?: InferLiteralDef<def[key]>;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** Object-literal-only inference for fluent branches; never inspects embedded schema types. */
|
|
144
|
+
export type InferObjectLiteral<def extends object> = Simplify<
|
|
145
|
+
LiteralRequiredProperties<def> & LiteralOptionalProperties<def>
|
|
146
|
+
>;
|
|
147
|
+
|
|
148
|
+
/** Infer the validated output type produced by a definition. */
|
|
149
|
+
export type InferDef<def = unknown> = def extends { infer: infer output }
|
|
150
|
+
? output
|
|
151
|
+
: def extends string
|
|
152
|
+
? InferString<def>
|
|
153
|
+
: def extends readonly [infer element, "[]"]
|
|
154
|
+
? InferDef<element>[]
|
|
155
|
+
: def extends object
|
|
156
|
+
? InferObject<def>
|
|
157
|
+
: unknown;
|
|
158
|
+
|
|
159
|
+
/** Input-side defaults and morphs are not distinguished yet. */
|
|
160
|
+
export type InferDefIn<def = unknown> = InferDef<def>;
|
package/src/interp.ts
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tree-walking validator used for a schema's first few calls (before the JIT
|
|
3
|
+
* compiler kicks in) and as the fallback for IR shapes the compiler declines.
|
|
4
|
+
*
|
|
5
|
+
* Semantics must stay in lockstep with `compile.ts`:
|
|
6
|
+
* - success returns the output value; the input is returned as-is unless the
|
|
7
|
+
* schema morphs (defaults, `"+": "delete"`, embedded stepped schemas), in
|
|
8
|
+
* which case a fresh object/array is produced and the input is untouched
|
|
9
|
+
* - failure returns an `OmpErrors` with a single fast-fail entry
|
|
10
|
+
*/
|
|
11
|
+
import { MISSING, OmpErrors } from "./errors";
|
|
12
|
+
import { expectedOf, hasMorph, type IR } from "./ir";
|
|
13
|
+
|
|
14
|
+
const own = Object.prototype.hasOwnProperty;
|
|
15
|
+
|
|
16
|
+
/** Validate `value` against `ir`; returns output value or `OmpErrors`. */
|
|
17
|
+
export function walk(ir: IR, value: unknown): unknown {
|
|
18
|
+
const path: PropertyKey[] = [];
|
|
19
|
+
const out = visit(ir, value, path);
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function fail(path: PropertyKey[], expected: string, data: unknown): OmpErrors {
|
|
24
|
+
return OmpErrors.single([...path], expected, data);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Pure predicate used for union-member scanning (no morphs, no errors). */
|
|
28
|
+
function checks(ir: IR, v: unknown): boolean {
|
|
29
|
+
switch (ir.k) {
|
|
30
|
+
case "unknown":
|
|
31
|
+
return true;
|
|
32
|
+
case "null":
|
|
33
|
+
return v === null;
|
|
34
|
+
case "undefined":
|
|
35
|
+
return v === undefined;
|
|
36
|
+
case "boolean":
|
|
37
|
+
return typeof v === "boolean";
|
|
38
|
+
case "bigint":
|
|
39
|
+
return typeof v === "bigint";
|
|
40
|
+
case "anyobject":
|
|
41
|
+
return typeof v === "object" && v !== null;
|
|
42
|
+
case "string":
|
|
43
|
+
return (
|
|
44
|
+
typeof v === "string" &&
|
|
45
|
+
(ir.min === undefined || v.length >= ir.min) &&
|
|
46
|
+
(ir.max === undefined || v.length <= ir.max) &&
|
|
47
|
+
(!ir.url || URL.canParse(v))
|
|
48
|
+
);
|
|
49
|
+
case "number":
|
|
50
|
+
return (
|
|
51
|
+
typeof v === "number" &&
|
|
52
|
+
(!ir.int || Number.isInteger(v)) &&
|
|
53
|
+
(ir.min === undefined || (ir.xmin ? v > ir.min : v >= ir.min)) &&
|
|
54
|
+
(ir.max === undefined || (ir.xmax ? v < ir.max : v <= ir.max))
|
|
55
|
+
);
|
|
56
|
+
case "lit":
|
|
57
|
+
return v === ir.v;
|
|
58
|
+
case "union":
|
|
59
|
+
return ir.members.some(m => checks(m, v));
|
|
60
|
+
case "array": {
|
|
61
|
+
if (!Array.isArray(v)) return false;
|
|
62
|
+
if (ir.min !== undefined && v.length < ir.min) return false;
|
|
63
|
+
if (ir.max !== undefined && v.length > ir.max) return false;
|
|
64
|
+
for (const el of v) if (!checks(ir.el, el)) return false;
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
case "object": {
|
|
68
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
|
|
69
|
+
const rec = v as Record<string, unknown>;
|
|
70
|
+
for (const p of ir.props) {
|
|
71
|
+
const present = p.key in rec;
|
|
72
|
+
if (!present) {
|
|
73
|
+
if (!p.opt && !p.hasDefault) return false;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (!checks(p.val, rec[p.key])) return false;
|
|
77
|
+
}
|
|
78
|
+
if (ir.index) {
|
|
79
|
+
for (const key in rec) {
|
|
80
|
+
if (own.call(rec, key) && !checks(ir.index, rec[key])) return false;
|
|
81
|
+
}
|
|
82
|
+
} else if (ir.extras === "reject") {
|
|
83
|
+
for (const key in rec) {
|
|
84
|
+
if (!own.call(rec, key)) continue;
|
|
85
|
+
let declared = false;
|
|
86
|
+
for (const p of ir.props) {
|
|
87
|
+
if (p.key === key) {
|
|
88
|
+
declared = true;
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (!declared) return false;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
case "sub":
|
|
98
|
+
return !(ir.schema.run(v) instanceof OmpErrors);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function visit(ir: IR, v: unknown, path: PropertyKey[]): unknown {
|
|
103
|
+
switch (ir.k) {
|
|
104
|
+
case "sub": {
|
|
105
|
+
const out = ir.schema.run(v);
|
|
106
|
+
if (out instanceof OmpErrors) {
|
|
107
|
+
return path.length === 0 ? out : prefixAll(out, path);
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
case "union": {
|
|
112
|
+
// fast path: any pure member matching returns the input unchanged
|
|
113
|
+
for (const m of ir.members) {
|
|
114
|
+
if (m.k !== "sub" && checks(m, v)) {
|
|
115
|
+
if (hasMorph(m)) break;
|
|
116
|
+
return v;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
for (const m of ir.members) {
|
|
120
|
+
if (m.k === "sub" || hasMorph(m)) {
|
|
121
|
+
const out = visit(m, v, path);
|
|
122
|
+
if (!(out instanceof OmpErrors)) return out;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return unionFail(ir, v, path);
|
|
126
|
+
}
|
|
127
|
+
case "array": {
|
|
128
|
+
if (!Array.isArray(v)) return fail(path, "an array", v);
|
|
129
|
+
if (ir.min !== undefined && v.length < ir.min) return fail(path, `at least length ${ir.min}`, v);
|
|
130
|
+
if (ir.max !== undefined && v.length > ir.max) return fail(path, `at most length ${ir.max}`, v);
|
|
131
|
+
if (!hasMorph(ir.el)) {
|
|
132
|
+
for (let i = 0; i < v.length; i++) {
|
|
133
|
+
if (!checks(ir.el, v[i])) {
|
|
134
|
+
path.push(i);
|
|
135
|
+
const err = visit(ir.el, v[i], path);
|
|
136
|
+
path.pop();
|
|
137
|
+
return err instanceof OmpErrors ? err : fail([...path, i], expectedOf(ir.el), v[i]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return v;
|
|
141
|
+
}
|
|
142
|
+
const out = new Array<unknown>(v.length);
|
|
143
|
+
for (let i = 0; i < v.length; i++) {
|
|
144
|
+
path.push(i);
|
|
145
|
+
const el = visit(ir.el, v[i], path);
|
|
146
|
+
path.pop();
|
|
147
|
+
if (el instanceof OmpErrors) return el;
|
|
148
|
+
out[i] = el;
|
|
149
|
+
}
|
|
150
|
+
return out;
|
|
151
|
+
}
|
|
152
|
+
case "object": {
|
|
153
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return fail(path, "an object", v);
|
|
154
|
+
const rec = v as Record<string, unknown>;
|
|
155
|
+
const morph = hasMorph(ir);
|
|
156
|
+
let out: Record<string, unknown> | undefined;
|
|
157
|
+
if (morph) {
|
|
158
|
+
if (ir.extras === "delete" && !ir.index) {
|
|
159
|
+
out = {};
|
|
160
|
+
} else {
|
|
161
|
+
out = { ...rec };
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
for (const p of ir.props) {
|
|
165
|
+
if (!(p.key in rec)) {
|
|
166
|
+
if (p.hasDefault && out) {
|
|
167
|
+
// defFactory guarantees a callable default payload
|
|
168
|
+
const payload = p.def;
|
|
169
|
+
out[p.key] = p.defFactory && typeof payload === "function" ? payload() : payload;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (p.opt || p.hasDefault) continue;
|
|
173
|
+
path.push(p.key);
|
|
174
|
+
const err = fail(path, expectedOf(p.val), MISSING);
|
|
175
|
+
path.pop();
|
|
176
|
+
return err;
|
|
177
|
+
}
|
|
178
|
+
path.push(p.key);
|
|
179
|
+
const res = visit(p.val, rec[p.key], path);
|
|
180
|
+
path.pop();
|
|
181
|
+
if (res instanceof OmpErrors) return res;
|
|
182
|
+
if (out) out[p.key] = res;
|
|
183
|
+
}
|
|
184
|
+
if (ir.index) {
|
|
185
|
+
for (const key in rec) {
|
|
186
|
+
if (!own.call(rec, key)) continue;
|
|
187
|
+
path.push(key);
|
|
188
|
+
const res = visit(ir.index, rec[key], path);
|
|
189
|
+
path.pop();
|
|
190
|
+
if (res instanceof OmpErrors) return res;
|
|
191
|
+
if (out) out[key] = res;
|
|
192
|
+
}
|
|
193
|
+
} else if (ir.extras === "reject") {
|
|
194
|
+
for (const key in rec) {
|
|
195
|
+
if (!own.call(rec, key)) continue;
|
|
196
|
+
let declared = false;
|
|
197
|
+
for (const p of ir.props) {
|
|
198
|
+
if (p.key === key) {
|
|
199
|
+
declared = true;
|
|
200
|
+
break;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!declared) {
|
|
204
|
+
path.push(key);
|
|
205
|
+
const err = fail(path, "removed (undeclared key)", rec[key]);
|
|
206
|
+
path.pop();
|
|
207
|
+
return err;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return out ?? v;
|
|
212
|
+
}
|
|
213
|
+
default:
|
|
214
|
+
return checks(ir, v) ? v : fail(path, expectedOf(ir), v);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function prefixAll(errs: OmpErrors, path: PropertyKey[]): OmpErrors {
|
|
219
|
+
for (let i = path.length - 1; i >= 0; i--) errs.prefix(path[i]);
|
|
220
|
+
return errs;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Detailed failure for a union: descend into the member the value was clearly
|
|
225
|
+
* aimed at — unique runtime-kind match, else an object member whose literal
|
|
226
|
+
* discriminant property (e.g. `type: "'computer_call'"`) equals the value's —
|
|
227
|
+
* for a precise nested error (paths, narrow messages) instead of the coarse
|
|
228
|
+
* "A or B" expectation.
|
|
229
|
+
*/
|
|
230
|
+
export function unionFail(ir: IR & { k: "union" }, v: unknown, path: PropertyKey[]): OmpErrors {
|
|
231
|
+
let best: IR | undefined;
|
|
232
|
+
for (const m of ir.members) {
|
|
233
|
+
const base = m.k === "sub" ? m.schema.ir : m;
|
|
234
|
+
if (!kindMatches(base, v)) continue;
|
|
235
|
+
if (best !== undefined) {
|
|
236
|
+
best = undefined;
|
|
237
|
+
break;
|
|
238
|
+
}
|
|
239
|
+
best = m;
|
|
240
|
+
}
|
|
241
|
+
if (best === undefined) best = discriminate(ir.members, v);
|
|
242
|
+
if (best) {
|
|
243
|
+
const out = visit(best, v, path);
|
|
244
|
+
if (out instanceof OmpErrors) return out;
|
|
245
|
+
}
|
|
246
|
+
return fail(path, expectedOf(ir), v);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Pick the sole object member whose literal-typed property matches the value's. */
|
|
250
|
+
function discriminate(members: IR[], v: unknown): IR | undefined {
|
|
251
|
+
if (typeof v !== "object" || v === null || Array.isArray(v)) return undefined;
|
|
252
|
+
const rec = v as Record<string, unknown>;
|
|
253
|
+
let match: IR | undefined;
|
|
254
|
+
for (const m of members) {
|
|
255
|
+
const base = m.k === "sub" ? m.schema.ir : m;
|
|
256
|
+
if (base.k !== "object") continue;
|
|
257
|
+
for (const p of base.props) {
|
|
258
|
+
if (p.val.k !== "lit" || rec[p.key] !== p.val.v) continue;
|
|
259
|
+
if (match !== undefined) return undefined; // ambiguous
|
|
260
|
+
match = m;
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return match;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/** True when a value's runtime shape could only be aimed at this member. */
|
|
268
|
+
function kindMatches(base: IR, v: unknown): boolean {
|
|
269
|
+
switch (base.k) {
|
|
270
|
+
case "array":
|
|
271
|
+
return Array.isArray(v);
|
|
272
|
+
case "object":
|
|
273
|
+
case "anyobject":
|
|
274
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
275
|
+
case "string":
|
|
276
|
+
return typeof v === "string";
|
|
277
|
+
case "number":
|
|
278
|
+
return typeof v === "number";
|
|
279
|
+
default:
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
}
|