diff --git a/package-lock.json b/package-lock.json index 120d4b5d..28bd5810 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.32.28", + "version": "0.32.29", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.32.28", + "version": "0.32.29", "license": "MIT", "devDependencies": { "@arethetypeswrong/cli": "^0.13.2", diff --git a/package.json b/package.json index f84806a1..e8e4708e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.32.28", + "version": "0.32.29", "description": "Json Schema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts index ecaa5e16..67512251 100644 --- a/src/type/keyof/index.ts +++ b/src/type/keyof/index.ts @@ -27,5 +27,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export * from './keyof-from-mapped-result' +export * from './keyof-property-entries' export * from './keyof-property-keys' export * from './keyof' diff --git a/src/type/keyof/keyof-property-entries.ts b/src/type/keyof/keyof-property-entries.ts new file mode 100644 index 00000000..f8eb236f --- /dev/null +++ b/src/type/keyof/keyof-property-entries.ts @@ -0,0 +1,42 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2024 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { IndexFromPropertyKeys } from '../indexed/indexed' +import { KeyOfPropertyKeys } from './keyof-property-keys' +import { TSchema } from '../schema/index' + +/** + * `[Utility]` Resolves an array of keys and schemas from the given schema. This method is faster + * than obtaining the keys and resolving each individually via indexing. This method was written + * accellerate Intersect and Union encoding. + */ +export function KeyOfPropertyEntries(schema: TSchema): [key: string, schema: TSchema][] { + const keys = KeyOfPropertyKeys(schema) as string[] + const schemas = IndexFromPropertyKeys(schema, keys) + return keys.map((_, index) => [keys[index], schemas[index]]) +} diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts index 00edd6d9..e6ffb9a5 100644 --- a/src/value/transform/decode.ts +++ b/src/value/transform/decode.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' -import { KeyOfPropertyKeys } from '../../type/keyof/index' +import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' import { Index } from '../../type/indexed/index' import { Deref } from '../deref/index' import { Check } from '../check/index' @@ -98,10 +98,11 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a // prettier-ignore function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { if (!IsStandardObject(value) || IsValueType(value)) return Default(schema, path, value) - const knownKeys = KeyOfPropertyKeys(schema) as string[] + const knownEntries = KeyOfPropertyEntries(schema) + const knownKeys = knownEntries.map(entry => entry[0]) const knownProperties = { ...value } as Record - for(const key of knownKeys) if(key in knownProperties) { - knownProperties[key] = Visit(Index(schema, [key]), references, `${path}/${key}`, knownProperties[key]) + for(const [knownKey, knownSchema] of knownEntries) if(knownKey in knownProperties) { + knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey]) } if (!IsTransform(schema.unevaluatedProperties)) { return Default(schema, path, knownProperties) diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts index 80ee9856..39a43cf9 100644 --- a/src/value/transform/encode.ts +++ b/src/value/transform/encode.ts @@ -29,7 +29,7 @@ THE SOFTWARE. import { Kind, TransformKind } from '../../type/symbols/index' import { TypeBoxError } from '../../type/error/index' import { ValueError } from '../../errors/index' -import { KeyOfPropertyKeys } from '../../type/keyof/index' +import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index' import { Index } from '../../type/indexed/index' import { Deref } from '../deref/index' import { Check } from '../check/index' @@ -99,10 +99,11 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) { const defaulted = Default(schema, path, value) if (!IsStandardObject(value) || IsValueType(value)) return defaulted - const knownKeys = KeyOfPropertyKeys(schema) as string[] + const knownEntries = KeyOfPropertyEntries(schema) + const knownKeys = knownEntries.map(entry => entry[0]) const knownProperties = { ...defaulted } as Record - for(const key of knownKeys) if(key in knownProperties) { - knownProperties[key] = Visit(Index(schema, [key]), references, `${path}/${key}`, knownProperties[key]) + for(const [knownKey, knownSchema] of knownEntries) if(knownKey in knownProperties) { + knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey]) } if (!IsTransform(schema.unevaluatedProperties)) { return Default(schema, path, knownProperties) diff --git a/src/value/value/value.ts b/src/value/value/value.ts index 1228fa82..944d27d6 100644 --- a/src/value/value/value.ts +++ b/src/value/value/value.ts @@ -26,7 +26,7 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { TransformDecode, TransformEncode, TransformDecodeCheckError, TransformEncodeCheckError } from '../transform/index' +import { HasTransform, TransformDecode, TransformEncode, TransformDecodeCheckError, TransformEncodeCheckError } from '../transform/index' import { Mutate as MutateValue, type Mutable } from '../mutate/index' import { Hash as HashValue } from '../hash/index' import { Equal as EqualValue } from '../equal/index' @@ -95,7 +95,7 @@ export function Decode>(schema: T, value: export function Decode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] if (!Check(schema, references, value)) throw new TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) - return TransformDecode(schema, references, value) + return HasTransform(schema, references) ? TransformDecode(schema, references, value) : value } /** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ export function Default(schema: TSchema, references: TSchema[], value: unknown): unknown @@ -112,7 +112,7 @@ export function Encode>(schema: T, value: /** Encodes a value or throws if error */ export function Encode(...args: any[]) { const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const encoded = TransformEncode(schema, references, value) + const encoded = HasTransform(schema, references) ? TransformEncode(schema, references, value) : value if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, encoded, Errors(schema, references, encoded).First()!) return encoded }