From 2dd0f0d25a5471541646809c70876877bb9cae47 Mon Sep 17 00:00:00 2001 From: Scott Lott Date: Fri, 12 Jan 2024 10:52:56 -0800 Subject: [PATCH 1/3] Added code to enable type printing --- src/struct.ts | 3 +++ src/structs/coercions.ts | 6 +++++- src/structs/types.ts | 44 +++++++++++++++++++++++----------------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/struct.ts b/src/struct.ts index b482fd3b..9b02df5e 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -11,6 +11,7 @@ export class Struct { readonly TYPE!: T type: string schema: S + extend?: any coercer: (value: unknown, context: Context) => unknown validator: (value: unknown, context: Context) => Iterable refiner: (value: T, context: Context) => Iterable @@ -22,6 +23,7 @@ export class Struct { constructor(props: { type: string schema: S + extend?: any coercer?: Coercer validator?: Validator refiner?: Refiner @@ -37,6 +39,7 @@ export class Struct { } = props this.type = type + this.extend = props.extend; this.schema = schema this.entries = entries this.coercer = coercer diff --git a/src/structs/coercions.ts b/src/structs/coercions.ts index 6dca4ece..f578c299 100644 --- a/src/structs/coercions.ts +++ b/src/structs/coercions.ts @@ -42,7 +42,7 @@ export function defaulted( strict?: boolean } = {} ): Struct { - return coerce(struct, unknown(), (x) => { + const result = coerce(struct, unknown(), (x) => { const f = typeof fallback === 'function' ? fallback() : fallback if (x === undefined) { @@ -67,6 +67,10 @@ export function defaulted( return x }) + + result.extend = ["def", fallback]; + + return result; } /** diff --git a/src/structs/types.ts b/src/structs/types.ts index 8bcc0ef3..b5e07b2e 100644 --- a/src/structs/types.ts +++ b/src/structs/types.ts @@ -142,13 +142,17 @@ export function func(): Struct { export function instance( Class: T -): Struct, null> { - return define('instance', (value) => { - return ( - value instanceof Class || - `Expected a \`${Class.name}\` instance, but received: ${print(value)}` - ) - }) +): Struct, T> { + return new Struct({ + type: 'instance', + schema: Class, + validator: (value) => { + return ( + value instanceof Class || + `Expected a \`${Class.name}\` instance, but received: ${print(value)}` + ) + } + }); } /** @@ -170,10 +174,10 @@ export function integer(): Struct { export function intersection( Structs: [A, ...B] -): Struct & UnionToIntersection[number]>, null> { +): Struct & UnionToIntersection[number]>, [A, ...B]> { return new Struct({ type: 'intersection', - schema: null, + schema: Structs, *entries(value, ctx) { for (const S of Structs) { yield* S.entries(value, ctx) @@ -225,11 +229,11 @@ export function map(): Struct, null> export function map( Key: Struct, Value: Struct -): Struct, null> +): Struct, [K, V]> export function map(Key?: Struct, Value?: Struct): any { return new Struct({ type: 'map', - schema: null, + schema: [Key, Value], *entries(value) { if (Key && Value && value instanceof Map) { for (const [k, v] of value.entries()) { @@ -265,6 +269,7 @@ export function never(): Struct { export function nullable(struct: Struct): Struct { return new Struct({ ...struct, + extend: ["null"], validator: (value, ctx) => value === null || struct.validator(value, ctx), refiner: (value, ctx) => value === null || struct.refiner(value, ctx), }) @@ -332,6 +337,7 @@ export function object(schema?: S): any { export function optional(struct: Struct): Struct { return new Struct({ ...struct, + extend: ["?"], validator: (value, ctx) => value === undefined || struct.validator(value, ctx), refiner: (value, ctx) => value === undefined || struct.refiner(value, ctx), @@ -348,10 +354,10 @@ export function optional(struct: Struct): Struct { export function record( Key: Struct, Value: Struct -): Struct, null> { +): Struct, [Struct, Struct]> { return new Struct({ type: 'record', - schema: null, + schema: [Key, Value], *entries(value) { if (isObject(value)) { for (const k in value) { @@ -388,11 +394,11 @@ export function regexp(): Struct { */ export function set(): Struct, null> -export function set(Element: Struct): Struct, null> +export function set(Element: Struct): Struct, Struct> export function set(Element?: Struct): any { return new Struct({ type: 'set', - schema: null, + schema: Element, *entries(value) { if (Element && value instanceof Set) { for (const v of value) { @@ -432,12 +438,12 @@ export function string(): Struct { export function tuple( Structs: [A, ...B] -): Struct<[Infer, ...InferStructTuple], null> { +): Struct<[Infer, ...InferStructTuple], [A, ...B]> { const Never = never() return new Struct({ type: 'tuple', - schema: null, + schema: Structs, *entries(value) { if (Array.isArray(value)) { const length = Math.max(Structs.length, value.length) @@ -494,11 +500,11 @@ export function type( export function union( Structs: [A, ...B] -): Struct | InferStructTuple[number], null> { +): Struct | InferStructTuple[number], [A, ...B]> { const description = Structs.map((s) => s.type).join(' | ') return new Struct({ type: 'union', - schema: null, + schema: Structs, coercer(value) { for (const S of Structs) { const [error, coerced] = S.validate(value, { coerce: true }) From 013c886d832db43ad3ebdd4230dbc26cf899483f Mon Sep 17 00:00:00 2001 From: Scott Lott Date: Fri, 12 Jan 2024 12:35:34 -0800 Subject: [PATCH 2/3] working on fixing tests --- src/struct.ts | 3 ++- src/utils.ts | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/struct.ts b/src/struct.ts index 9b02df5e..b598bd77 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -33,13 +33,14 @@ export class Struct { type, schema, validator, + extend, refiner, coercer = (value: unknown) => value, entries = function* () {}, } = props this.type = type - this.extend = props.extend; + this.extend = extend this.schema = schema this.entries = entries this.coercer = coercer diff --git a/src/utils.ts b/src/utils.ts index 92df9755..5ef9b4ed 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -339,6 +339,7 @@ export type If = B extends true ? Then : Else */ export type StructSchema = [T] extends [string | undefined | null] + ? [T] extends [IsMatch] ? null : [T] extends [IsUnion] @@ -354,18 +355,21 @@ export type StructSchema = [T] extends [string | undefined | null] ? [T] extends [IsExactMatch] ? null : T + : T extends RegExp | Date | Function + ? null + : T extends Set + ? Struct + : T extends Record + ? [Struct, Struct] + : T extends Map + ? [A, B] : T extends | bigint | symbol | undefined | null - | Function - | Date | Error - | RegExp - | Map | WeakMap - | Set | WeakSet | Promise ? null From 76335c8afa58510b930828fc250ff78d9306ee3d Mon Sep 17 00:00:00 2001 From: Scott Lott Date: Fri, 12 Jan 2024 13:04:51 -0800 Subject: [PATCH 3/3] tests are running now --- src/structs/types.ts | 12 ++++++------ src/utils.ts | 24 +++++++++++------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/structs/types.ts b/src/structs/types.ts index b5e07b2e..0682c59e 100644 --- a/src/structs/types.ts +++ b/src/structs/types.ts @@ -143,16 +143,16 @@ export function func(): Struct { export function instance( Class: T ): Struct, T> { - return new Struct({ - type: 'instance', - schema: Class, + return new Struct({ + type: 'instance', + schema: Class, validator: (value) => { return ( value instanceof Class || `Expected a \`${Class.name}\` instance, but received: ${print(value)}` ) } - }); + }) } /** @@ -229,7 +229,7 @@ export function map(): Struct, null> export function map( Key: Struct, Value: Struct -): Struct, [K, V]> +): Struct, [Struct, Struct]> export function map(Key?: Struct, Value?: Struct): any { return new Struct({ type: 'map', @@ -549,4 +549,4 @@ export function union( export function unknown(): Struct { return define('unknown', () => true) -} +} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 5ef9b4ed..27df5c7d 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -339,9 +339,8 @@ export type If = B extends true ? Then : Else */ export type StructSchema = [T] extends [string | undefined | null] - ? [T] extends [IsMatch] - ? null + ? any : [T] extends [IsUnion] ? EnumSchema : T @@ -355,33 +354,32 @@ export type StructSchema = [T] extends [string | undefined | null] ? [T] extends [IsExactMatch] ? null : T - : T extends RegExp | Date | Function - ? null - : T extends Set - ? Struct - : T extends Record - ? [Struct, Struct] - : T extends Map - ? [A, B] + : T extends Map + ? [Struct, Struct] + : T extends Set + ? Struct : T extends | bigint | symbol | undefined | null + | Function + | Date | Error + | RegExp | WeakMap | WeakSet | Promise ? null : T extends Array ? T extends IsTuple - ? null + ? any : Struct : T extends object ? T extends IsRecord - ? null + ? [any, any] : { [K in keyof T]: Describe } - : null + : any /** * A schema for tuple structs.