mirror of
https://github.com/zoriya/typebox.git
synced 2025-12-06 06:46:10 +00:00
Revision 0.33.5 (#959)
* Use ExactOptionalProperty Policy on Decode | Encode * Version
This commit is contained in:
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.33.4",
|
||||
"version": "0.33.5",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.33.4",
|
||||
"version": "0.33.5",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@arethetypeswrong/cli": "^0.13.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.33.4",
|
||||
"version": "0.33.5",
|
||||
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
|
||||
"keywords": [
|
||||
"typescript",
|
||||
|
||||
@@ -52,24 +52,24 @@ export namespace TypeSystemPolicy {
|
||||
export let AllowNaN: boolean = false
|
||||
/** Sets whether `null` should validate for void types. The default is `false` */
|
||||
export let AllowNullVoid: boolean = false
|
||||
/** Asserts this value using the ExactOptionalPropertyTypes policy */
|
||||
/** Checks this value using the ExactOptionalPropertyTypes policy */
|
||||
export function IsExactOptionalProperty(value: Record<keyof any, unknown>, key: string) {
|
||||
return ExactOptionalPropertyTypes ? key in value : value[key] !== undefined
|
||||
}
|
||||
/** Asserts this value using the AllowArrayObjects policy */
|
||||
/** Checks this value using the AllowArrayObjects policy */
|
||||
export function IsObjectLike(value: unknown): value is Record<keyof any, unknown> {
|
||||
const isObject = IsObject(value)
|
||||
return AllowArrayObject ? isObject : isObject && !IsArray(value)
|
||||
}
|
||||
/** Asserts this value as a record using the AllowArrayObjects policy */
|
||||
/** Checks this value as a record using the AllowArrayObjects policy */
|
||||
export function IsRecordLike(value: unknown): value is Record<keyof any, unknown> {
|
||||
return IsObjectLike(value) && !(value instanceof Date) && !(value instanceof Uint8Array)
|
||||
}
|
||||
/** Asserts this value using the AllowNaN policy */
|
||||
/** Checks this value using the AllowNaN policy */
|
||||
export function IsNumberLike(value: unknown): value is number {
|
||||
return AllowNaN ? IsNumber(value) : Number.isFinite(value)
|
||||
}
|
||||
/** Asserts this value using the AllowVoidNull policy */
|
||||
/** Checks this value using the AllowVoidNull policy */
|
||||
export function IsVoidLike(value: unknown): value is void {
|
||||
const isUndefined = IsUndefined(value)
|
||||
return AllowNullVoid ? isUndefined || value === null : isUndefined
|
||||
|
||||
@@ -55,6 +55,12 @@ import type { TUndefined } from '../../type/undefined/index'
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol, HasPropertyKey } from '../guard/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// TypeGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsOptional } from '../../type/guard/kind'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Conversions
|
||||
// ------------------------------------------------------------------
|
||||
@@ -193,8 +199,9 @@ function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[], value: any): unknown {
|
||||
if(!IsObject(value)) return value
|
||||
for(const key of Object.getOwnPropertyNames(schema.properties)) {
|
||||
value[key] = Visit(schema.properties[key], references, value[key])
|
||||
for(const propertyKey of Object.getOwnPropertyNames(schema.properties)) {
|
||||
if(!HasPropertyKey(value, propertyKey)) continue
|
||||
value[propertyKey] = Visit(schema.properties[propertyKey], references, value[propertyKey])
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -26,11 +26,11 @@ THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { TypeSystemPolicy } from '../../system/policy'
|
||||
import { Kind, TransformKind } from '../../type/symbols/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
import { ValueError } from '../../errors/index'
|
||||
import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index'
|
||||
import { Index } from '../../type/indexed/index'
|
||||
import { Deref } from '../deref/index'
|
||||
import { Check } from '../check/index'
|
||||
|
||||
@@ -48,11 +48,11 @@ import type { TUnion } from '../../type/union/index'
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsObject, IsArray, IsValueType } from '../guard/index'
|
||||
import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// TypeGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsTransform, IsSchema } from '../../type/guard/type'
|
||||
import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type'
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
@@ -121,9 +121,18 @@ function FromNot(schema: TNot, references: TSchema[], path: string, value: any)
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[], path: string, value: any) {
|
||||
if (!IsObject(value)) return Default(schema, path, value)
|
||||
const knownKeys = KeyOfPropertyKeys(schema)
|
||||
const knownKeys = KeyOfPropertyKeys(schema) as string[]
|
||||
const knownProperties = { ...value } as Record<PropertyKey, unknown>
|
||||
for(const key of knownKeys) if(key in knownProperties) {
|
||||
for(const key of knownKeys) {
|
||||
if(!HasPropertyKey(knownProperties, key)) continue
|
||||
// if the property value is undefined, but the target is not, nor does it satisfy exact optional
|
||||
// property policy, then we need to continue. This is a special case for optional property handling
|
||||
// where a transforms wrapped in a optional modifiers should not run.
|
||||
if(IsUndefinedValue(knownProperties[key]) && (
|
||||
!IsUndefined(schema.properties[key]) ||
|
||||
TypeSystemPolicy.IsExactOptionalProperty(knownProperties, key)
|
||||
)) continue
|
||||
// decode property
|
||||
knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key])
|
||||
}
|
||||
if (!IsSchema(schema.additionalProperties)) {
|
||||
|
||||
@@ -26,11 +26,11 @@ THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { TypeSystemPolicy } from '../../system/policy'
|
||||
import { Kind, TransformKind } from '../../type/symbols/index'
|
||||
import { TypeBoxError } from '../../type/error/index'
|
||||
import { ValueError } from '../../errors/index'
|
||||
import { KeyOfPropertyKeys, KeyOfPropertyEntries } from '../../type/keyof/index'
|
||||
import { Index } from '../../type/indexed/index'
|
||||
import { Deref } from '../deref/index'
|
||||
import { Check } from '../check/index'
|
||||
|
||||
@@ -48,11 +48,11 @@ import type { TUnion } from '../../type/union/index'
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsObject, IsArray, IsValueType } from '../guard/index'
|
||||
import { HasPropertyKey, IsObject, IsArray, IsValueType, IsUndefined as IsUndefinedValue } from '../guard/index'
|
||||
// ------------------------------------------------------------------
|
||||
// TypeGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsTransform, IsSchema } from '../../type/guard/type'
|
||||
import { IsTransform, IsSchema, IsUndefined } from '../../type/guard/type'
|
||||
// ------------------------------------------------------------------
|
||||
// Errors
|
||||
// ------------------------------------------------------------------
|
||||
@@ -126,7 +126,16 @@ function FromObject(schema: TObject, references: TSchema[], path: string, value:
|
||||
if (!IsObject(defaulted)) return defaulted
|
||||
const knownKeys = KeyOfPropertyKeys(schema) as string[]
|
||||
const knownProperties = { ...defaulted } as Record<PropertyKey, unknown>
|
||||
for(const key of knownKeys) if(key in knownProperties) {
|
||||
for(const key of knownKeys) {
|
||||
if(!HasPropertyKey(knownProperties, key)) continue
|
||||
// if the property value is undefined, but the target is not, nor does it satisfy exact optional
|
||||
// property policy, then we need to continue. This is a special case for optional property handling
|
||||
// where a transforms wrapped in a optional modifiers should not run.
|
||||
if(IsUndefinedValue(knownProperties[key]) && (
|
||||
!IsUndefined(schema.properties[key]) ||
|
||||
TypeSystemPolicy.IsExactOptionalProperty(knownProperties, key)
|
||||
)) continue
|
||||
// encode property
|
||||
knownProperties[key] = Visit(schema.properties[key], references, `${path}/${key}`, knownProperties[key])
|
||||
}
|
||||
if (!IsSchema(schema.additionalProperties)) {
|
||||
|
||||
@@ -21,4 +21,9 @@ describe('value/convert/Object', () => {
|
||||
const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' })
|
||||
Assert.IsEqual(R, { x: 42, y: true, z: 'hello' })
|
||||
})
|
||||
it('Should not convert missing properties', () => {
|
||||
const T = Type.Object({ x: Type.Number() })
|
||||
const R = Value.Convert(T, { })
|
||||
Assert.IsEqual(R, { })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import * as Encoder from './_encoder'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
import { TypeSystemPolicy } from '@sinclair/typebox/system'
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
|
||||
@@ -156,7 +158,7 @@ describe('value/transform/Object', () => {
|
||||
// https://github.com/sinclairzx81/typebox/issues/859
|
||||
// ----------------------------------------------------------------
|
||||
it('Should decode for nested transform with renamed property', () => {
|
||||
class User { constructor(public name: string, public createdAt: Date) {} }
|
||||
class User { constructor(public name: string, public createdAt: Date) { } }
|
||||
const TDate = Type.Transform(Type.Number())
|
||||
.Decode(v => new Date(v))
|
||||
.Encode(v => v.getTime())
|
||||
@@ -185,7 +187,116 @@ describe('value/transform/Object', () => {
|
||||
const B = Object.create(null); B.x = '1'
|
||||
const D = Value.Decode(T, A)
|
||||
const E = Value.Encode(T, B)
|
||||
Assert.IsEqual(D, { x: '1' })
|
||||
Assert.IsEqual(D, { x: '1' })
|
||||
Assert.IsEqual(E, { x: 1 })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// https://github.com/sinclairzx81/typebox/issues/958
|
||||
// ----------------------------------------------------------------
|
||||
it('Should not decode missing optional properties 0', () => {
|
||||
let Invoked = false
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => { Invoked = true; return value })
|
||||
.Encode((value) => value)
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Decode(T, { value: 'foo' })
|
||||
Assert.IsEqual(D, { value: 'foo' })
|
||||
Assert.IsTrue(Invoked)
|
||||
})
|
||||
it('Should not decode missing optional properties 1', () => {
|
||||
let Invoked = false
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => { Invoked = true; return value })
|
||||
.Encode((value) => value)
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Decode(T, {})
|
||||
Assert.IsEqual(D, {})
|
||||
Assert.IsFalse(Invoked)
|
||||
})
|
||||
it('Should not decode missing optional properties 2', () => {
|
||||
let Invoked = false
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => { Invoked = true; return value })
|
||||
.Encode((value) => value)
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Decode(T, { value: undefined })
|
||||
Assert.IsEqual(D, { value: undefined })
|
||||
Assert.IsFalse(Invoked)
|
||||
})
|
||||
it('Should not decode missing optional properties 3 (ExactOptionalPropertyTypes)', () => {
|
||||
let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes]
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = true
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => { Invoked = true; return value })
|
||||
.Encode((value) => value)
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Decode(T, {})
|
||||
Assert.IsEqual(D, {})
|
||||
Assert.IsFalse(Invoked)
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = Revert
|
||||
})
|
||||
it('Should not decode missing optional properties 4 (ExactOptionalPropertyTypes)', () => {
|
||||
let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes]
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = true
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => { Invoked = true; return value })
|
||||
.Encode((value) => value)
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
Assert.Throws(() => Value.Decode(T, { value: undefined }))
|
||||
Assert.IsFalse(Invoked)
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = Revert
|
||||
})
|
||||
it('Should not encode missing optional properties 0', () => {
|
||||
let Invoked = false
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => { Invoked = true; return value })
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Encode(T, { value: 'foo' })
|
||||
Assert.IsEqual(D, { value: 'foo' })
|
||||
Assert.IsTrue(Invoked)
|
||||
})
|
||||
it('Should not encode missing optional properties 1', () => {
|
||||
let Invoked = false
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => { Invoked = true; return value })
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Encode(T, {})
|
||||
Assert.IsEqual(D, {})
|
||||
Assert.IsFalse(Invoked)
|
||||
})
|
||||
it('Should not encode missing optional properties 2', () => {
|
||||
let Invoked = false
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => { Invoked = true; return value })
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Encode(T, { value: undefined })
|
||||
Assert.IsEqual(D, { value: undefined })
|
||||
Assert.IsFalse(Invoked)
|
||||
})
|
||||
it('Should not encode missing optional properties 3 (ExactOptionalPropertyTypes)', () => {
|
||||
let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes]
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = true
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => { Invoked = true; return value })
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
const D = Value.Encode(T, {})
|
||||
Assert.IsEqual(D, {})
|
||||
Assert.IsFalse(Invoked)
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = Revert
|
||||
})
|
||||
it('Should not encode missing optional properties 4 (ExactOptionalPropertyTypes)', () => {
|
||||
let [Invoked, Revert] = [false, TypeSystemPolicy.ExactOptionalPropertyTypes]
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = true
|
||||
const S = Type.Transform(Type.RegExp(/foo/))
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => { Invoked = true; return value })
|
||||
const T = Type.Object({ value: Type.Optional(S) })
|
||||
Assert.Throws(() => Value.Encode(T, { value: undefined }))
|
||||
Assert.IsFalse(Invoked)
|
||||
TypeSystemPolicy.ExactOptionalPropertyTypes = Revert
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user