Skip to content

Commit 934dc9a

Browse files
authored
Send Kind To TypeRegistry Function (#522)
1 parent 93547e5 commit 934dc9a

File tree

19 files changed

+244
-115
lines changed

19 files changed

+244
-115
lines changed

package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sinclair/typebox",
3-
"version": "0.30.2",
3+
"version": "0.30.3",
44
"description": "JSONSchema Type Builder with Static Type Resolution for TypeScript",
55
"keywords": [
66
"typescript",

src/compiler/compiler.ts

+8-3
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,9 @@ export namespace TypeCompiler {
375375
yield IsVoidCheck(value)
376376
}
377377
function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator<string> {
378-
yield `kind('${schema[Types.Kind]}', ${value})`
378+
const instance = state.instances.size
379+
state.instances.set(instance, schema)
380+
yield `kind('${schema[Types.Kind]}', ${instance}, ${value})`
379381
}
380382
function* Visit<T extends Types.TSchema>(schema: T, references: Types.TSchema[], value: string, useHoisting: boolean = true): IterableIterator<string> {
381383
const references_ = ValueGuard.IsString(schema.$id) ? [...references, schema] : references
@@ -470,6 +472,7 @@ export namespace TypeCompiler {
470472
language: 'javascript', // target language
471473
functions: new Map<string, string>(), // local functions
472474
variables: new Map<string, string>(), // local variables
475+
instances: new Map<number, Types.TKind>() // exterior kind instances
473476
}
474477
// -------------------------------------------------------------------
475478
// Compiler Factory
@@ -533,6 +536,7 @@ export namespace TypeCompiler {
533536
state.language = options.language
534537
state.variables.clear()
535538
state.functions.clear()
539+
state.instances.clear()
536540
if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema)
537541
for (const schema of references) if (!Types.TypeGuard.TSchema(schema)) throw new TypeCompilerTypeGuardError(schema)
538542
return Build(schema, references, options)
@@ -541,8 +545,9 @@ export namespace TypeCompiler {
541545
export function Compile<T extends Types.TSchema>(schema: T, references: Types.TSchema[] = []): TypeCheck<T> {
542546
const generatedCode = Code(schema, references, { language: 'javascript' })
543547
const compiledFunction = globalThis.Function('kind', 'format', 'hash', generatedCode)
544-
function typeRegistryFunction(kind: string, value: unknown) {
545-
if (!Types.TypeRegistry.Has(kind)) return false
548+
function typeRegistryFunction(kind: string, instance: number, value: unknown) {
549+
if (!Types.TypeRegistry.Has(kind) || !state.instances.has(instance)) return false
550+
const schema = state.instances.get(instance)
546551
const checkFunc = Types.TypeRegistry.Get(kind)!
547552
return checkFunc(schema, value)
548553
}

test/runtime/compiler/custom.ts

-26
This file was deleted.

test/runtime/compiler/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import './async-iterator'
44
import './bigint'
55
import './boolean'
66
import './composite'
7-
import './custom'
87
import './date'
98
import './unicode'
109
import './enum'
1110
import './integer'
1211
import './intersect'
1312
import './iterator'
1413
import './keyof'
14+
import './kind'
1515
import './literal'
1616
import './modifier'
1717
import './never'

test/runtime/compiler/kind.ts

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import { TypeRegistry, Type, Kind, TSchema } from '@sinclair/typebox'
2+
import { TypeCompiler } from '@sinclair/typebox/compiler'
3+
import { Ok, Fail } from './validate'
4+
import { Assert } from '../assert'
5+
6+
describe('type/compiler/Kind', () => {
7+
// ------------------------------------------------------------
8+
// Fixtures
9+
// ------------------------------------------------------------
10+
beforeEach(() => TypeRegistry.Set('PI', (_, value) => value === Math.PI))
11+
afterEach(() => TypeRegistry.Delete('PI'))
12+
// ------------------------------------------------------------
13+
// Tests
14+
// ------------------------------------------------------------
15+
it('Should validate', () => {
16+
const T = Type.Unsafe({ [Kind]: 'PI' })
17+
Ok(T, Math.PI)
18+
})
19+
it('Should not validate', () => {
20+
const T = Type.Unsafe({ [Kind]: 'PI' })
21+
Fail(T, Math.PI * 2)
22+
})
23+
it('Should validate in object', () => {
24+
const T = Type.Object({
25+
x: Type.Unsafe({ [Kind]: 'PI' }),
26+
})
27+
Ok(T, { x: Math.PI })
28+
})
29+
it('Should not validate in object', () => {
30+
const T = Type.Object({
31+
x: Type.Unsafe({ [Kind]: 'PI' }),
32+
})
33+
Fail(T, { x: Math.PI * 2 })
34+
})
35+
it('Should validate in array', () => {
36+
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
37+
Ok(T, [Math.PI])
38+
})
39+
it('Should not validate in array', () => {
40+
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
41+
Fail(T, [Math.PI * 2])
42+
})
43+
it('Should validate in tuple', () => {
44+
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
45+
Ok(T, [Math.PI])
46+
})
47+
it('Should not validate in tuple', () => {
48+
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
49+
Fail(T, [Math.PI * 2])
50+
})
51+
// ------------------------------------------------------------
52+
// Instances
53+
// ------------------------------------------------------------
54+
it('Should receive kind instance on registry callback', () => {
55+
const stack: string[] = []
56+
TypeRegistry.Set('Kind', (schema: unknown) => {
57+
// prettier-ignore
58+
return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string')
59+
? (() => { stack.push(schema.$id); return true })()
60+
: false
61+
})
62+
const A = { [Kind]: 'Kind', $id: 'A' } as TSchema
63+
const B = { [Kind]: 'Kind', $id: 'B' } as TSchema
64+
const T = Type.Object({ a: A, b: B })
65+
const C = TypeCompiler.Compile(T)
66+
const R = C.Check({ a: null, b: null })
67+
Assert.IsTrue(R)
68+
Assert.IsEqual(stack[0], 'A')
69+
Assert.IsEqual(stack[1], 'B')
70+
TypeRegistry.Delete('Kind')
71+
})
72+
})

test/runtime/type/guard/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import './integer'
1616
import './intersect'
1717
import './iterator'
1818
import './keyof'
19+
import './kind'
1920
import './literal'
2021
import './lowercase'
2122
import './not'

test/runtime/type/guard/kind.ts

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { TypeGuard, Kind } from '@sinclair/typebox'
2+
import { Assert } from '../../assert/index'
3+
4+
describe('type/guard/TKind', () => {
5+
it('Should guard 1', () => {
6+
const T = { [Kind]: 'Kind' }
7+
Assert.IsTrue(TypeGuard.TKind(T))
8+
})
9+
it('Should guard 2', () => {
10+
const T = {}
11+
Assert.IsFalse(TypeGuard.TKind(T))
12+
})
13+
})

test/runtime/value/cast/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import './async-iterator'
44
import './bigint'
55
import './boolean'
66
import './composite'
7-
import './custom'
87
import './date'
98
import './enum'
109
import './integer'
1110
import './intersect'
1211
import './iterator'
1312
import './keyof'
13+
import './kind'
1414
import './literal'
1515
import './never'
1616
import './not'

test/runtime/value/cast/custom.ts renamed to test/runtime/value/cast/kind.ts

+10-8
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ import { Value } from '@sinclair/typebox/value'
22
import { Type, Kind, TypeRegistry } from '@sinclair/typebox'
33
import { Assert } from '../../assert/index'
44

5-
describe('value/cast/Custom', () => {
6-
before(() => {
7-
TypeRegistry.Set('CustomCast', (schema, value) => value === 'hello' || value === 'world')
8-
})
9-
after(() => {
10-
TypeRegistry.Clear()
11-
})
12-
const T = Type.Unsafe({ [Kind]: 'CustomCast', default: 'hello' })
5+
describe('value/cast/Kind', () => {
6+
// ---------------------------------------------------------
7+
// Fixtures
8+
// ---------------------------------------------------------
9+
before(() => TypeRegistry.Set('Kind', (schema, value) => value === 'hello' || value === 'world'))
10+
after(() => TypeRegistry.Clear())
11+
// ---------------------------------------------------------
12+
// Tests
13+
// ---------------------------------------------------------
14+
const T = Type.Unsafe({ [Kind]: 'Kind', default: 'hello' })
1315
const E = 'hello'
1416
it('Should upcast from string', () => {
1517
const value = 'hello'

test/runtime/value/check/custom.ts

-29
This file was deleted.

test/runtime/value/check/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import './async-iterator'
44
import './bigint'
55
import './boolean'
66
import './composite'
7-
import './custom'
87
import './date'
98
import './enum'
109
import './integer'
1110
import './intersect'
1211
import './iterator'
1312
import './keyof'
13+
import './kind'
1414
import './literal'
1515
import './never'
1616
import './not'

test/runtime/value/check/kind.ts

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { Value } from '@sinclair/typebox/value'
2+
import { TypeRegistry, Type, Kind, TSchema } from '@sinclair/typebox'
3+
import { Assert } from '../../assert'
4+
5+
describe('value/check/Kind', () => {
6+
// ------------------------------------------------------------
7+
// Fixtures
8+
// ------------------------------------------------------------
9+
beforeEach(() => TypeRegistry.Set('PI', (_, value) => value === Math.PI))
10+
afterEach(() => TypeRegistry.Delete('PI'))
11+
// ------------------------------------------------------------
12+
// Tests
13+
// ------------------------------------------------------------
14+
it('Should validate', () => {
15+
const T = Type.Unsafe({ [Kind]: 'PI' })
16+
Assert.IsTrue(Value.Check(T, Math.PI))
17+
})
18+
it('Should not validate', () => {
19+
const T = Type.Unsafe({ [Kind]: 'PI' })
20+
Assert.IsFalse(Value.Check(T, Math.PI * 2))
21+
})
22+
it('Should validate in object', () => {
23+
const T = Type.Object({
24+
x: Type.Unsafe({ [Kind]: 'PI' }),
25+
})
26+
Assert.IsTrue(Value.Check(T, { x: Math.PI }))
27+
})
28+
it('Should not validate in object', () => {
29+
const T = Type.Object({
30+
x: Type.Unsafe({ [Kind]: 'PI' }),
31+
})
32+
Assert.IsFalse(Value.Check(T, { x: Math.PI * 2 }))
33+
})
34+
it('Should validate in array', () => {
35+
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
36+
Assert.IsTrue(Value.Check(T, [Math.PI]))
37+
})
38+
it('Should not validate in array', () => {
39+
const T = Type.Array(Type.Unsafe({ [Kind]: 'PI' }))
40+
Assert.IsFalse(Value.Check(T, [Math.PI * 2]))
41+
})
42+
it('Should validate in tuple', () => {
43+
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
44+
Assert.IsTrue(Value.Check(T, [Math.PI]))
45+
})
46+
it('Should not validate in tuple', () => {
47+
const T = Type.Tuple([Type.Unsafe({ [Kind]: 'PI' })])
48+
Assert.IsFalse(Value.Check(T, [Math.PI * 2]))
49+
})
50+
// ------------------------------------------------------------
51+
// Instances
52+
// ------------------------------------------------------------
53+
it('Should receive kind instance on registry callback', () => {
54+
const stack: string[] = []
55+
TypeRegistry.Set('Kind', (schema: unknown) => {
56+
// prettier-ignore
57+
return (typeof schema === 'object' && schema !== null && Kind in schema && schema[Kind] === 'Kind' && '$id' in schema && typeof schema.$id === 'string')
58+
? (() => { stack.push(schema.$id); return true })()
59+
: false
60+
})
61+
const A = { [Kind]: 'Kind', $id: 'A' } as TSchema
62+
const B = { [Kind]: 'Kind', $id: 'B' } as TSchema
63+
const T = Type.Object({ a: A, b: B })
64+
const R = Value.Check(T, { a: null, b: null })
65+
Assert.IsTrue(R)
66+
Assert.IsEqual(stack[0], 'A')
67+
Assert.IsEqual(stack[1], 'B')
68+
TypeRegistry.Delete('Kind')
69+
})
70+
})

test/runtime/value/convert/custom.ts

-24
This file was deleted.

test/runtime/value/convert/index.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import './bigint'
55
import './boolean'
66
import './composite'
77
import './constructor'
8-
import './custom'
8+
import './kind'
99
import './date'
1010
import './enum'
1111
import './function'

0 commit comments

Comments
 (0)