From c94a44c6ded395db8aaacc362719b1f980c1cf89 Mon Sep 17 00:00:00 2001 From: sinclair Date: Sun, 23 Jul 2023 01:14:04 +0900 Subject: [PATCH] use readOnly annotation on Type.Readonly | support writeOnly annotation on schema options --- example/index.ts | 19 +++++++----------- readme.md | 11 +---------- src/typebox.ts | 50 +++++++++++++++++++++++------------------------- 3 files changed, 32 insertions(+), 48 deletions(-) diff --git a/example/index.ts b/example/index.ts index 212a96b44..7efab4d4b 100644 --- a/example/index.ts +++ b/example/index.ts @@ -10,18 +10,13 @@ import { Type, TypeGuard, Kind, Static, TSchema, Optional, Readonly } from '@sin const ReadonlyOptional = (schema: T) => Type.Readonly(Type.Optional(schema)) const A = Type.Object({ - x: Type.Number(), - y: Type.Number(), + x: Type.Readonly(Type.Number({ writeOnly: true })), + y: Type.Number({ readOnly: true }), z: Type.Number(), }) -const B = Type.Object({ - x: Type.Number(), - y: Type.Number(), - z: Type.Number(), -}) -const I = Type.Intersect([A, B]) -const T = Type.Partial(I) -console.log(I) -console.log(T) -type T = Static +import Ajv from 'ajv' + +const ajv = new Ajv() + +console.log(ajv.validate(A, { x: 1, y: 2, z: 3 })) diff --git a/readme.md b/readme.md index 4bf1b532b..0cc041781 100644 --- a/readme.md +++ b/readme.md @@ -611,21 +611,12 @@ TypeBox provides modifiers that allow schema properties to be statically inferre │ Type.String() │ } │ properties: { │ │ ) │ │ name: { │ │ }) │ │ type: 'string' │ +│ │ │ readOnly: true │ │ │ │ } │ │ │ │ }, │ │ │ │ required: ['name'] │ │ │ │ } │ │ │ │ │ -├────────────────────────────────┼─────────────────────────────┼────────────────────────────────┤ -│ const T = Type.Object({ │ type T = { │ const T = { │ -│ name: Type.ReadonlyOptional( │ readonly name?: string │ type: 'object', │ -│ Type.String() │ } │ properties: { │ -│ ) │ │ name: { │ -│ }) │ │ type: 'string' │ -│ │ │ } │ -│ │ │ } │ -│ │ │ } │ -│ │ │ │ └────────────────────────────────┴─────────────────────────────┴────────────────────────────────┘ ``` diff --git a/src/typebox.ts b/src/typebox.ts index e4263c846..2952c3d9e 100644 --- a/src/typebox.ts +++ b/src/typebox.ts @@ -137,6 +137,10 @@ export interface SchemaOptions { default?: any /** Example values matching this schema */ examples?: any + /** Optional annotation for readOnly */ + readOnly?: boolean + /** Optional annotation for writeOnly */ + writeOnly?: boolean [prop: string]: any } export interface TKind { @@ -2457,14 +2461,14 @@ export class StandardTypeBuilder extends TypeBuilder { // ------------------------------------------------------------------------ // Modifiers // ------------------------------------------------------------------------ + /** `[Standard]` Creates a Readonly property */ + public Readonly(schema: T): TReadonly { + return { ...TypeClone.Clone(schema, {}), [Readonly]: 'Readonly', readOnly: true } + } /** `[Standard]` Creates an Optional property */ public Optional(schema: T): TOptional { return { ...TypeClone.Clone(schema, {}), [Optional]: 'Optional' } } - /** `[Standard]` Creates a Readonly property */ - public Readonly(schema: T): TReadonly { - return { ...TypeClone.Clone(schema, {}), [Readonly]: 'Readonly' } - } // ------------------------------------------------------------------------ // Types // ------------------------------------------------------------------------ @@ -2656,17 +2660,14 @@ export class StandardTypeBuilder extends TypeBuilder { } /** `[Standard]` Creates a mapped type where all properties are Optional */ public Partial(schema: T, options: ObjectOptions = {}): TPartial { - return ObjectMap.Map( - schema, - (object) => { - const properties = globalThis.Object.keys(object.properties).reduce((acc, key) => { - return { ...acc, [key]: this.Optional(object.properties[key]) } - }, {} as TProperties) - const { required: _, ...rest } = schema - return this.Object(properties, { ...rest }) - }, - options, - ) + // prettier-ignore + return ObjectMap.Map(schema, (object) => { + const properties = globalThis.Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { + return { ...acc, [key]: this.Optional(object.properties[key]) } + }, {} as TProperties) + const { required: _, ...rest } = schema + return this.Object(properties, { ...rest }) + }, options) } /** `[Standard]` Creates a mapped type whose keys are picked from the given type */ public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick @@ -2744,17 +2745,14 @@ export class StandardTypeBuilder extends TypeBuilder { } /** `[Standard]` Creates a mapped type where all properties are Required */ public Required(schema: T, options: SchemaOptions = {}): TRequired { - return ObjectMap.Map( - schema, - (object) => { - const properties = globalThis.Object.keys(object.properties).reduce((acc, key) => { - const { [Optional]: _, ...rest } = object.properties[key] - return { ...acc, [key]: rest } - }, {} as TProperties) - return this.Object(properties, { ...schema }) - }, - options, - ) + // prettier-ignore + return ObjectMap.Map(schema, (object) => { + const properties = globalThis.Object.keys(object.properties).reduce((acc, key) => { + const { [Optional]: _, ...rest } = object.properties[key] + return { ...acc, [key]: rest } + }, {} as TProperties) + return this.Object(properties, { ...schema }) + }, options) } /** `[Standard]` Returns a schema array which allows types to compose with the JavaScript spread operator */ public Rest(schema: T): TRest {