Skip to content

Commit d86f254

Browse files
committed
feat: support lazy, close #60
1 parent b1ea0fc commit d86f254

File tree

3 files changed

+34
-4
lines changed

3 files changed

+34
-4
lines changed

packages/core/src/index.ts

+24-2
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,12 @@ declare global {
5555
is<T>(constructor: Constructor<T>): Schema<T>
5656
array<X>(inner: X): Schema<TypeS<X>[], TypeT<X>[]>
5757
dict<X, Y extends Schema<any, string> = Schema<string>>(inner: X, sKey?: Y): Schema<Dict<TypeS<X>, TypeS<Y>>, Dict<TypeT<X>, TypeT<Y>>>
58-
tuple<X extends readonly any[]>(list: X): Schema<TupleS<X>, TupleT<X>>
58+
tuple<const X extends readonly any[]>(list: X): Schema<TupleS<X>, TupleT<X>>
5959
object<X extends Dict>(dict: X): Schema<ObjectS<X>, ObjectT<X>>
6060
union<const X>(list: readonly X[]): Schema<TypeS<X>, TypeT<X>>
6161
intersect<const X>(list: readonly X[]): Schema<IntersectS<X>, IntersectT<X>>
6262
transform<X, T>(inner: X, callback: (value: TypeS<X>, options: Schemastery.Options) => T, preserve?: boolean): Schema<TypeS<X>, T>
63+
lazy<X extends Schema>(callback: () => X): X
6364
ValidationError: typeof ValidationError
6465
}
6566

@@ -102,6 +103,7 @@ declare global {
102103
dict?: Dict<Schema>
103104
bits?: Dict<number>
104105
callback?: Function
106+
builder?: Function
105107
value?: T
106108
refs?: Dict<Schema>
107109
preserve?: boolean
@@ -384,7 +386,7 @@ Schema.resolve = function resolve(data, schema, options = {}, strict = false) {
384386
if (!schema) return [data]
385387
if (options.ignore?.(data, schema)) return [data]
386388

387-
if (isNullable(data)) {
389+
if (isNullable(data) && schema.type !== 'lazy') {
388390
if (schema.meta.required) throw new ValidationError(`missing required value`, options)
389391
let current = schema
390392
let fallback = schema.meta.default
@@ -427,6 +429,18 @@ Schema.from = function from(source: any) {
427429
}
428430
}
429431

432+
Schema.lazy = function lazy(builder) {
433+
const toJSON = () => {
434+
if (!schema.inner![kSchema]) {
435+
schema.inner = schema.builder!()
436+
schema.inner!.meta = { ...schema.meta, ...schema.inner!.meta }
437+
}
438+
return schema.inner!.toJSON()
439+
}
440+
const schema = new Schema({ type: 'lazy', builder, inner: { toJSON } as any })
441+
return schema as any
442+
}
443+
430444
Schema.natural = function natural() {
431445
return Schema.number().step(1).min(0)
432446
}
@@ -479,6 +493,14 @@ Schema.arrayBuffer = function arrayBuffer(encoding?: 'hex' | 'base64') {
479493
])
480494
}
481495

496+
Schema.extend('lazy', (data, schema, options, strict) => {
497+
if (!schema.inner![kSchema]) {
498+
schema.inner = schema.builder!()
499+
schema.inner!.meta = { ...schema.meta, ...schema.inner!.meta }
500+
}
501+
return Schema.resolve(data, schema.inner!, options, strict)
502+
})
503+
482504
Schema.extend('any', (data) => {
483505
return [data]
484506
})

packages/core/tests/index.spec.ts

+2
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ describe('Schema API', () => {
8181
bar: Schema.union([1, 2, 3]),
8282
})
8383

84+
// @ts-expect-error
8485
expect(config({ foo: 0, bar: 2 })).to.deep.equal({ foo: 1, bar: 2 })
86+
// @ts-expect-error
8587
expect(() => config({ foo: 2, bar: 0 })).to.throw()
8688
})
8789
})

packages/core/tests/misc.spec.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ describe('Miscellaneous', () => {
1313
})
1414

1515
test('serialization (recursive)', () => {
16-
const Node = Schema.object({ id: Number })
17-
Node.set('children', Schema.array(Node))
16+
interface Node {
17+
id: number
18+
children: Node[]
19+
}
20+
21+
const Node: Schema<Node> = Schema.lazy(() => {
22+
return Schema.object({ id: Number, children: Schema.array(Node) })
23+
}).role('node')
1824
const validate = new Schema(JSON.parse(JSON.stringify(Node)))
1925

2026
expect(validate({ id: 1 })).to.deep.equal({ id: 1, children: [] })

0 commit comments

Comments
 (0)