From 9c42cbc1e00caaed670d12de36e73021b2afc1a0 Mon Sep 17 00:00:00 2001 From: sinclairzx81 Date: Thu, 31 Aug 2023 05:35:49 +0900 Subject: [PATCH] Revision 0.31.8 (#566) * Discard on Mapped Object Types * Discard Identifier Tests --- package-lock.json | 4 ++-- package.json | 2 +- src/typebox.ts | 22 ++++++++++--------- test/runtime/type/guard/omit.ts | 34 +++++++++++++++++++---------- test/runtime/type/guard/partial.ts | 34 +++++++++++++++++++---------- test/runtime/type/guard/pick.ts | 34 +++++++++++++++++++---------- test/runtime/type/guard/required.ts | 34 +++++++++++++++++++---------- 7 files changed, 103 insertions(+), 61 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3870c30..8333c62 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@sinclair/typebox", - "version": "0.31.7", + "version": "0.31.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.7", + "version": "0.31.8", "license": "MIT", "devDependencies": { "@sinclair/hammer": "^0.17.1", diff --git a/package.json b/package.json index 8b54777..a50bc67 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.7", + "version": "0.31.8", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", diff --git a/src/typebox.ts b/src/typebox.ts index 7a3fa3e..bd4190a 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -2842,10 +2842,12 @@ export class TypeBuilder { protected Throw(message: string): never { throw new TypeBuilderError(message) } - /** `[Internal]` Discards a property key from the given schema */ - protected Discard(schema: TSchema, key: PropertyKey): TSchema { - const { [key as any]: _, ...rest } = schema - return rest as TSchema + /** `[Internal]` Discards property keys from the given record type */ + protected Discard(record: Record, keys: PropertyKey[]) { + return keys.reduce((acc, key) => { + const { [key as any]: _, ...rest } = acc + return rest + }, record) as any } /** `[Json]` Omits compositing symbols from this schema */ public Strict(schema: T): T { @@ -3082,7 +3084,7 @@ export class JsonTypeBuilder extends TypeBuilder { public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { if (ValueGuard.IsArray(object.required)) { object.required = object.required.filter((key: string) => !keys.includes(key as any)) if (object.required.length === 0) delete object.required @@ -3096,11 +3098,11 @@ export class JsonTypeBuilder extends TypeBuilder { /** `[Json]` Constructs a type where all properties are optional */ public Partial(schema: T, options: ObjectOptions = {}): TPartial { // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { return { ...acc, [key]: this.Optional(object.properties[key]) } }, {} as TProperties) - return this.Object(properties, this.Discard(object, 'required') /* object used as options to retain other constraints */) + return this.Object(properties, this.Discard(object, ['required']) /* object used as options to retain other constraints */) }, options) } /** `[Json]` Constructs a type whose keys are picked from the given type */ @@ -3117,7 +3119,7 @@ export class JsonTypeBuilder extends TypeBuilder { public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { const keys = KeyArrayResolver.Resolve(unresolved) // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { if (ValueGuard.IsArray(object.required)) { object.required = object.required.filter((key: any) => keys.includes(key)) if (object.required.length === 0) delete object.required @@ -3182,9 +3184,9 @@ export class JsonTypeBuilder extends TypeBuilder { /** `[Json]` Constructs a type where all properties are required */ public Required(schema: T, options: SchemaOptions = {}): TRequired { // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), Transform), (object) => { + return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { - return { ...acc, [key]: this.Discard(object.properties[key], Optional) as TSchema } + return { ...acc, [key]: this.Discard(object.properties[key], [Optional]) as TSchema } }, {} as TProperties) return this.Object(properties, object /* object used as options to retain other constraints */) }, options) diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index 1ceb07b..7b19d14 100644 --- a/test/runtime/type/guard/omit.ts +++ b/test/runtime/type/guard/omit.ts @@ -102,18 +102,28 @@ describe('type/guard/TOmit', () => { Assert.IsTrue(TypeGuard.TNumber(T.properties.ad)) Assert.IsEqual(T.required, ['ad']) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Omit(A, ['x'], { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Omit(A, ['x']) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Omit(S, ['x']) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Omit(S, ['x']) + Assert.IsFalse(Transform in R) }) }) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts index 8cb963b..cd248bb 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/partial.ts @@ -40,18 +40,28 @@ describe('type/guard/TPartial', () => { Assert.IsEqual(T.anyOf[0].required, undefined) Assert.IsEqual(T.anyOf[1].required, undefined) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Partial(A, { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Partial(A) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Partial(S) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Partial(S) + Assert.IsFalse(Transform in R) }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index b25094a..0fcd905 100644 --- a/test/runtime/type/guard/pick.ts +++ b/test/runtime/type/guard/pick.ts @@ -104,18 +104,28 @@ describe('type/guard/TPick', () => { Assert.IsTrue(TypeGuard.TNumber(T.properties.ac)) Assert.IsEqual(T.required, ['ab', 'ac']) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Pick(A, ['x'], { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Pick(A, ['x']) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Pick(S, ['x']) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Pick(S, ['x']) + Assert.IsFalse(Transform in R) }) }) diff --git a/test/runtime/type/guard/required.ts b/test/runtime/type/guard/required.ts index 67c0b38..5af8134 100644 --- a/test/runtime/type/guard/required.ts +++ b/test/runtime/type/guard/required.ts @@ -37,18 +37,28 @@ describe('type/guard/TRequired', () => { Assert.IsEqual(T.anyOf[0].required, ['x']) Assert.IsEqual(T.anyOf[1].required, ['y']) }) + // ---------------------------------------------------------------- + // Discard + // ---------------------------------------------------------------- + it('Should override $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Required(A, { $id: 'T' }) + Assert.IsEqual(T.$id!, 'T') + }) + it('Should discard $id', () => { + const A = Type.Object({ x: Type.Number() }, { $id: 'A' }) + const T = Type.Required(A) + Assert.IsFalse('$id' in T) + }) it('Should discard transform', () => { - const S = Type.Transform( - Type.Object({ - x: Type.Number(), - y: Type.String(), - }), - { - Decode: (value) => value, - Encode: (value) => value, - }, - ) - const T = Type.Required(S) - Assert.IsFalse(Transform in T) + const T = Type.Object({ + x: Type.Number(), + y: Type.String(), + }) + const S = Type.Transform(T) + .Decode((value) => value) + .Encode((value) => value) + const R = Type.Required(S) + Assert.IsFalse(Transform in R) }) })