diff --git a/examples/index.ts b/examples/index.ts index f6b663723..ee83e5362 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -1,35 +1,15 @@ import { TypeSystem } from '@sinclair/typebox/system' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value, ValueGuard } from '@sinclair/typebox/value' -import { Type, TypeGuard, Kind, Static, TSchema } from '@sinclair/typebox' +import { Type, TypeGuard, Kind, Static, TSchema, KeyResolver } from '@sinclair/typebox' import { Run } from './benchmark' // Todo: Investigate Union Default (initialize interior types) // Todo: Implement Value.Clean() Tests -export const T = Type.Object({ - number: Type.Number(), - negNumber: Type.Number(), - maxNumber: Type.Number(), - string: Type.String(), - longString: Type.String(), - boolean: Type.Boolean(), - deeplyNested: Type.Object({ - foo: Type.String(), - num: Type.Number(), - bool: Type.Boolean(), - }), -}) -const A = Value.Create(T) +const T = Type.Intersect([Type.Number(), Type.Number()]) -const R = Run( - () => { - Value.Strict(T, A) - }, - { - iterations: 1_000_000, - }, -) +const R = Value.Strict(T, 1) console.log(R) diff --git a/src/value/strict.ts b/src/value/strict.ts index 9225b5dbf..2f9547e19 100644 --- a/src/value/strict.ts +++ b/src/value/strict.ts @@ -26,9 +26,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsString, IsObject, IsArray, IsUndefined } from './guard' +import { IsString, IsObject, IsArray, IsUndefined, IsValueType } from './guard' import { Check } from './check' import { Deref } from './deref' +import { Clone } from './clone' import * as Types from '../typebox' // -------------------------------------------------------------------------- @@ -45,22 +46,29 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: unknow return value.map((value) => Visit(schema.items, references, value)) } function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: unknown): any { - const values: any[] = schema.allOf.map((schema) => Visit(schema, references, value)) - return values.reduce((acc, value) => { - return IsObject(value) ? { ...acc, ...value } : value - }, {} as any) + const unevaluatedProperties = schema.unevaluatedProperties as Types.TSchema + const intersections = schema.allOf.map((schema) => Visit(schema, references, Clone(value))) + const composite = intersections.reduce((acc: any, value: any) => (IsObject(value) ? { ...acc, ...value } : value), {}) + // unevaluatedProperties can only be applied to objects + if (!IsObject(value) || !IsObject(composite) || !FastIsSchema(unevaluatedProperties)) return composite + const knownkeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) + for (const key of Object.getOwnPropertyNames(value)) { + if (knownkeys.includes(key)) continue + if (Check(unevaluatedProperties, references, value[key])) { + composite[key] = Visit(unevaluatedProperties, references, value[key]) + } + } + return composite } function TObject(schema: Types.TObject, references: Types.TSchema[], value: unknown): any { if (!IsObject(value)) return value const additionalProperties = schema.additionalProperties as Types.TSchema - const hasAdditionalProperties = FastIsSchema(additionalProperties) - const propertyKeys = Object.keys(value) - for (const key of propertyKeys) { + for (const key of Object.getOwnPropertyNames(value)) { if (key in schema.properties) { value[key] = Visit(schema.properties[key], references, value[key]) continue } - if (hasAdditionalProperties && Check(additionalProperties, references, value[key])) { + if (FastIsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) { value[key] = Visit(additionalProperties, references, value[key]) continue } @@ -71,16 +79,15 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: unkn function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: unknown): any { if (!IsObject(value)) return value const additionalProperties = schema.additionalProperties as Types.TSchema - const useAdditionalProperties = FastIsSchema(additionalProperties) - const [propertyKey, propertySchema] = Object.entries(schema.patternProperties)[0] const propertyKeys = Object.keys(value) + const [propertyKey, propertySchema] = Object.entries(schema.patternProperties)[0] const propertyKeyTest = new RegExp(propertyKey) for (const key of propertyKeys) { if (propertyKeyTest.test(key)) { value[key] = Visit(propertySchema, references, value[key]) continue } - if (useAdditionalProperties && Check(additionalProperties, references, value[key])) { + if (FastIsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) { value[key] = Visit(additionalProperties, references, value[key]) continue } diff --git a/test/runtime/value/strict/intersect.ts b/test/runtime/value/strict/intersect.ts index bbb94e963..5048ea58a 100644 --- a/test/runtime/value/strict/intersect.ts +++ b/test/runtime/value/strict/intersect.ts @@ -13,14 +13,14 @@ describe('value/strict/Intersect', () => { const R = Value.Strict(T, {}) Assert.IsEqual(R, {}) }) - // it('Should clean 3', () => { - // const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) - // const R = Value.Strict(T, { x: 1, y: 2 }) - // Assert.IsEqual(R, { x: 1, y: 2 }) - // }) - // it('Should clean 4', () => { - // const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) - // const R = Value.Strict(T, { x: 1, y: 2, z: 3 }) - // Assert.IsEqual(R, { x: 1, y: 2 }) - // }) + it('Should clean 3', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + const R = Value.Strict(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clean 4', () => { + const T = Type.Intersect([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + const R = Value.Strict(T, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) })