diff --git a/examples/index.ts b/examples/index.ts index 9d3d5fcb9..3aba4b866 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -6,34 +6,6 @@ import { Run } from './benchmark' // Todo: Investigate Union Default (initialize interior types) // Todo: Implement Value.Clean() Tests - -function ParseEnv(schema: T, value: unknown = process.env): Static { - const converted = Value.Convert(schema, value) - const defaulted = Value.Default(schema, converted) - const checked = Value.Check(schema, defaulted) - return checked - ? (Value.Clean(schema, defaulted) as Static) - : (() => { - const first = Value.Errors(schema, defaulted).First()! - throw new Error(`${first.message} ${first.path}. Value is ${first.value}`) - })() -} -console.log(process.env) - -const R = Run( - () => { - const E = ParseEnv( - Type.Object({ - PROCESSOR_ARCHITECTURE: Type.String(), - PROCESSOR_IDENTIFIER: Type.String(), - PROCESSOR_LEVEL: Type.Number(), - PROCESSOR_REVISION: Type.Number(), - }), - ) - }, - { - iterations: 2000, - }, -) - +const T = Type.Tuple([Type.Number(), Type.Number()]) +const R = Value.Clean(T, [1, 2, 3]) console.log(R) diff --git a/src/value/clean.ts b/src/value/clean.ts index 1c97ecfc9..43729bf69 100644 --- a/src/value/clean.ts +++ b/src/value/clean.ts @@ -60,7 +60,7 @@ function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value return composite } function TObject(schema: Types.TObject, references: Types.TSchema[], value: unknown): any { - if (!IsObject(value) || IsArray(value)) return value // IsArray escape for AllowArrayObject + if (!IsObject(value) || IsArray(value)) return value // Check IsArray for AllowArrayObject configuration const additionalProperties = schema.additionalProperties as Types.TSchema for (const key of Object.getOwnPropertyNames(value)) { if (key in schema.properties) { @@ -103,11 +103,14 @@ function TThis(schema: Types.TThis, references: Types.TSchema[], value: unknown) function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: unknown): any { if (!IsArray(value)) return value if (IsUndefined(schema.items)) return [] - const length = schema.items.length + const length = Math.min(value.length, schema.items.length) for (let i = 0; i < length; i++) { value[i] = Visit(schema.items[i], references, value[i]) } - return value.length > length ? value.splice(length) : value + // prettier-ignore + return value.length > length + ? value.slice(0, length) + : value } function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: unknown): any { for (const inner of schema.anyOf) { diff --git a/test/runtime/value/clean/index.ts b/test/runtime/value/clean/index.ts index 8cf7fa6d6..0343390fb 100644 --- a/test/runtime/value/clean/index.ts +++ b/test/runtime/value/clean/index.ts @@ -20,3 +20,10 @@ import './null' import './object' import './promise' import './record' +import './recursive' +import './ref' +import './regexp' +import './string' +import './symbol' +import './template-literal' +import './tuple' diff --git a/test/runtime/value/clean/record.ts b/test/runtime/value/clean/record.ts index 28cda1835..6ef341c15 100644 --- a/test/runtime/value/clean/record.ts +++ b/test/runtime/value/clean/record.ts @@ -77,14 +77,38 @@ describe('value/clean/Record', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean(), }) - const R = Value.Clean(T, {}) + const R = Value.Clean(T, { a: null }) Assert.IsEqual(R, {}) }) it('Should clean additional properties discard 3', () => { const T = Type.Record(Type.Number(), Type.String(), { additionalProperties: Type.Boolean(), }) - const R = Value.Clean(T, { 0: null }) + const R = Value.Clean(T, { a: null, 0: null }) Assert.IsEqual(R, { 0: null }) }) + // ---------------------------------------------------------------- + // Additional Properties Keep + // ---------------------------------------------------------------- + it('Should clean additional properties keep 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties keep 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: true }) + Assert.IsEqual(R, { a: true }) + }) + it('Should clean additional properties keep 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: true, 0: null }) + Assert.IsEqual(R, { a: true, 0: null }) + }) }) diff --git a/test/runtime/value/clean/recursive.ts b/test/runtime/value/clean/recursive.ts new file mode 100644 index 000000000..1a6bb6e82 --- /dev/null +++ b/test/runtime/value/clean/recursive.ts @@ -0,0 +1,112 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Recursive', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null }) + Assert.IsEqual(R, { id: null }) + }) + it('Should clean 3', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: null }) + Assert.IsEqual(R, { id: null, nodes: null }) + }) + it('Should clean 4', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: [] }) + Assert.IsEqual(R, { id: null, nodes: [] }) + }) + it('Should clean 5', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: [{ id: null }] }) + Assert.IsEqual(R, { id: null, nodes: [{ id: null }] }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null }) + Assert.IsEqual(R, { id: null }) + }) + it('Should clean discard 3', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: null }) + Assert.IsEqual(R, { id: null, nodes: null }) + }) + it('Should clean discard 4', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: [] }) + Assert.IsEqual(R, { id: null, nodes: [] }) + }) + it('Should clean discard 5', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: [{ u: null, id: null }] }) + Assert.IsEqual(R, { id: null, nodes: [{ id: null }] }) + }) +}) diff --git a/test/runtime/value/clean/ref.ts b/test/runtime/value/clean/ref.ts new file mode 100644 index 000000000..b06556159 --- /dev/null +++ b/test/runtime/value/clean/ref.ts @@ -0,0 +1,78 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Ref', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { x: null }) + Assert.IsEqual(R, { x: null }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { a: null }) + Assert.IsEqual(R, {}) + }) + it('Should clean discard 3', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { a: null, x: null }) + Assert.IsEqual(R, { x: null }) + }) +}) diff --git a/test/runtime/value/clean/regexp.ts b/test/runtime/value/clean/regexp.ts new file mode 100644 index 000000000..6b1d57421 --- /dev/null +++ b/test/runtime/value/clean/regexp.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/RegExp', () => { + it('Should clean 1', () => { + const T = Type.RegExp('') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/string.ts b/test/runtime/value/clean/string.ts new file mode 100644 index 000000000..aa5d75727 --- /dev/null +++ b/test/runtime/value/clean/string.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/String', () => { + it('Should clean 1', () => { + const T = Type.String() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/symbol.ts b/test/runtime/value/clean/symbol.ts new file mode 100644 index 000000000..62047d28e --- /dev/null +++ b/test/runtime/value/clean/symbol.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Symbol', () => { + it('Should clean 1', () => { + const T = Type.Symbol() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/template-literal.ts b/test/runtime/value/clean/template-literal.ts new file mode 100644 index 000000000..f76730548 --- /dev/null +++ b/test/runtime/value/clean/template-literal.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/TemplateLiteral', () => { + it('Should clean 1', () => { + const T = Type.TemplateLiteral('') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/tuple.ts b/test/runtime/value/clean/tuple.ts new file mode 100644 index 000000000..81618f0f9 --- /dev/null +++ b/test/runtime/value/clean/tuple.ts @@ -0,0 +1,110 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Tuple', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean 3', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, [1, 2]) + Assert.IsEqual(R, [1, 2]) + }) + it('Should clean 4', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, [1, 2, 3]) + Assert.IsEqual(R, [1, 2]) + }) + // ---------------------------------------------------------------- + // Clean Deep + // ---------------------------------------------------------------- + it('Should clean deep 1', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean deep 2', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean deep 3', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1]) + Assert.IsEqual(R, [1]) + }) + it('Should clean deep 4', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, null]) + Assert.IsEqual(R, [1, null]) + }) + it('Should clean deep 5', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, { x: null }]) + Assert.IsEqual(R, [1, { x: null }]) + }) + it('Should clean deep 6', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, { u: null, x: null }]) + Assert.IsEqual(R, [1, { x: null }]) + }) + // ---------------------------------------------------------------- + // Clean Empty + // ---------------------------------------------------------------- + it('Should clean empty 1', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean empty 2', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean empty 3', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, [1]) + Assert.IsEqual(R, []) + }) +})