Comparing version 0.0.5 to 0.0.6
@@ -19,4 +19,14 @@ declare abstract class Type<T> { | ||
export declare type Infer<T extends AnyType> = T extends Type<infer K> ? Eval<K> : any; | ||
declare type StringOptions = Partial<{ | ||
pattern: RegExp; | ||
min: number; | ||
max: number; | ||
}>; | ||
declare class StringType extends Type<string> { | ||
private opts; | ||
constructor(opts?: StringOptions); | ||
parse(value: unknown): string; | ||
pattern(regexp: RegExp): this; | ||
min(x: number): this; | ||
max(x: number): this; | ||
} | ||
@@ -26,4 +36,12 @@ declare class BooleanType extends Type<boolean> { | ||
} | ||
declare type NumberOptions = Partial<{ | ||
min: number; | ||
max: number; | ||
}>; | ||
declare class NumberType extends Type<number> { | ||
private opts; | ||
constructor(opts?: NumberOptions); | ||
parse(value: unknown): number; | ||
min(x: number): this; | ||
max(x: number): this; | ||
} | ||
@@ -48,5 +66,7 @@ declare class UndefinedType extends Type<undefined> { | ||
}; | ||
declare type PathOptions = { | ||
suppressPathErrMsg?: boolean; | ||
}; | ||
declare type ObjectOptions = { | ||
allowUnknown?: boolean; | ||
suppressPathErrMsg?: boolean; | ||
}; | ||
@@ -57,10 +77,14 @@ declare class ObjectType<T extends object> extends Type<InferObjectShape<T>> { | ||
constructor(objectShape: T, opts?: ObjectOptions | undefined); | ||
parse(value: unknown, optOverrides?: ObjectOptions): InferObjectShape<T>; | ||
parse(value: unknown, optOverrides?: ObjectOptions & PathOptions): InferObjectShape<T>; | ||
getShapeKeys(): string[]; | ||
} | ||
declare class RecordType<T extends AnyType> extends Type<Record<string, Infer<T>>> { | ||
private readonly schema; | ||
constructor(schema: T); | ||
parse(value: unknown, opts?: PathOptions & ObjectOptions): Record<string, Infer<T>>; | ||
} | ||
declare class ArrayType<T extends AnyType> extends Type<Infer<T>[]> { | ||
private readonly schema; | ||
constructor(schema: T); | ||
parse(value: unknown, opts?: { | ||
suppressPathErrMsg: boolean; | ||
}): Infer<T>[]; | ||
parse(value: unknown, opts?: PathOptions): Infer<T>[]; | ||
} | ||
@@ -81,10 +105,16 @@ declare type TupleToUnion<T extends any[]> = T[number]; | ||
declare class IntersectionType<T extends AnyType, K extends AnyType> extends Type<Infer<T> & Infer<K>> { | ||
private readonly left; | ||
private readonly right; | ||
private readonly schemas; | ||
constructor(left: T, right: K); | ||
parse(value: unknown): Infer<T> & Infer<K>; | ||
} | ||
export declare const string: () => StringType; | ||
export declare const string: (opts?: Partial<{ | ||
pattern: RegExp; | ||
min: number; | ||
max: number; | ||
}> | undefined) => StringType; | ||
export declare const boolean: () => BooleanType; | ||
export declare const number: () => NumberType; | ||
export declare const number: (opts?: Partial<{ | ||
min: number; | ||
max: number; | ||
}> | undefined) => NumberType; | ||
export declare const unknown: () => UnknownType; | ||
@@ -96,2 +126,4 @@ export declare const literal: <T extends Literal>(literal: T) => LiteralType<T>; | ||
export declare const intersection: <T extends AnyType, K extends AnyType>(l: T, r: K) => IntersectionType<T, K>; | ||
export declare const record: <T extends AnyType>(type: T) => RecordType<T>; | ||
export declare const dictionary: <T extends AnyType>(type: T) => RecordType<UnionType<(UndefinedType | T)[]>>; | ||
declare const undefinedValue: () => UndefinedType; | ||
@@ -101,5 +133,12 @@ declare const nullValue: () => NullType; | ||
declare const _default: { | ||
string: () => StringType; | ||
string: (opts?: Partial<{ | ||
pattern: RegExp; | ||
min: number; | ||
max: number; | ||
}> | undefined) => StringType; | ||
boolean: () => BooleanType; | ||
number: () => NumberType; | ||
number: (opts?: Partial<{ | ||
min: number; | ||
max: number; | ||
}> | undefined) => NumberType; | ||
unknown: () => UnknownType; | ||
@@ -106,0 +145,0 @@ literal: <T extends Literal>(literal: T) => LiteralType<T>; |
@@ -51,2 +51,6 @@ "use strict"; | ||
class StringType extends Type { | ||
constructor(opts = {}) { | ||
super(); | ||
this.opts = opts; | ||
} | ||
parse(value) { | ||
@@ -56,4 +60,25 @@ if (typeof value !== 'string') { | ||
} | ||
if (typeof this.opts.min === 'number' && value.length < this.opts.min) { | ||
throw new ValidationError(`expected string to have length greater than or equal to ${this.opts.min} but had length ${value.length}`); | ||
} | ||
if (typeof this.opts.max === 'number' && value.length > this.opts.max) { | ||
throw new ValidationError(`expected string to have length less than or equal to ${this.opts.max} but had length ${value.length}`); | ||
} | ||
if (this.opts.pattern instanceof RegExp && !this.opts.pattern.test(value)) { | ||
throw new ValidationError(`expected string to match pattern ${this.opts.pattern} but did not`); | ||
} | ||
return value; | ||
} | ||
pattern(regexp) { | ||
this.opts.pattern = regexp; | ||
return this; | ||
} | ||
min(x) { | ||
this.opts.min = x; | ||
return this; | ||
} | ||
max(x) { | ||
this.opts.max = x; | ||
return this; | ||
} | ||
} | ||
@@ -69,2 +94,6 @@ class BooleanType extends Type { | ||
class NumberType extends Type { | ||
constructor(opts = {}) { | ||
super(); | ||
this.opts = opts; | ||
} | ||
parse(value) { | ||
@@ -74,4 +103,18 @@ if (typeof value !== 'number') { | ||
} | ||
if (typeof this.opts.min === 'number' && value < this.opts.min) { | ||
throw new ValidationError(`expected number to be greater than or equal to ${this.opts.min} but got ${value}`); | ||
} | ||
if (typeof this.opts.max === 'number' && value > this.opts.max) { | ||
throw new ValidationError(`expected number to be less than or equal to ${this.opts.max} but got ${value}`); | ||
} | ||
return value; | ||
} | ||
min(x) { | ||
this.opts.min = x; | ||
return this; | ||
} | ||
max(x) { | ||
this.opts.max = x; | ||
return this; | ||
} | ||
} | ||
@@ -112,2 +155,3 @@ class UndefinedType extends Type { | ||
} | ||
const getKeyShapesSymbol = Symbol.for('getKeyShapes'); | ||
class ObjectType extends Type { | ||
@@ -118,4 +162,6 @@ constructor(objectShape, opts) { | ||
this.opts = opts; | ||
//@ts-ignore | ||
this[getKeyShapesSymbol] = () => Object.keys(this.objectShape); | ||
} | ||
parse(value, optOverrides) { | ||
parse(value, optOverrides = {}) { | ||
if (typeof value !== 'object') { | ||
@@ -145,6 +191,3 @@ throw new ValidationError('expected type to be object but got ' + typeOf(value)); | ||
} | ||
if (keySchema instanceof ObjectType) { | ||
acc[key] = keySchema.parse(value[key], Object.assign(Object.assign({}, opts), { suppressPathErrMsg: true })); | ||
} | ||
else if (keySchema instanceof ArrayType) { | ||
if (keySchema instanceof ObjectType || keySchema instanceof ArrayType || keySchema instanceof RecordType) { | ||
acc[key] = keySchema.parse(value[key], { suppressPathErrMsg: true }); | ||
@@ -158,4 +201,3 @@ } | ||
const path = err.path ? [key, ...err.path] : [key]; | ||
const msg = opts.suppressPathErrMsg | ||
? err.message | ||
const msg = (opts === null || opts === void 0 ? void 0 : opts.suppressPathErrMsg) ? err.message | ||
: `error parsing object at path: "${prettyPrintPath(path)}" - ${err.message}`; | ||
@@ -167,3 +209,37 @@ throw new ValidationError(msg, path); | ||
} | ||
getShapeKeys() { | ||
return Object.keys(this.objectShape); | ||
} | ||
} | ||
class RecordType extends Type { | ||
constructor(schema) { | ||
super(); | ||
this.schema = schema; | ||
} | ||
parse(value, opts) { | ||
if (typeof value !== 'object') { | ||
throw new ValidationError('expected type to be object but got ' + typeOf(value)); | ||
} | ||
for (const key in value) { | ||
try { | ||
if (this.schema instanceof ObjectType) { | ||
this.schema.parse(value[key], { allowUnknown: opts === null || opts === void 0 ? void 0 : opts.allowUnknown, suppressPathErrMsg: true }); | ||
} | ||
else if (this.schema instanceof ArrayType || this.schema instanceof RecordType) { | ||
this.schema.parse(value[key], { suppressPathErrMsg: true }); | ||
} | ||
else { | ||
this.schema.parse(value[key]); | ||
} | ||
} | ||
catch (err) { | ||
const path = err.path ? [key, ...err.path] : [key]; | ||
const msg = (opts === null || opts === void 0 ? void 0 : opts.suppressPathErrMsg) ? err.message | ||
: `error parsing record at path "${prettyPrintPath(path)}" - ${err.message}`; | ||
throw new ValidationError(msg, path); | ||
} | ||
} | ||
return value; | ||
} | ||
} | ||
class ArrayType extends Type { | ||
@@ -222,7 +298,8 @@ constructor(schema) { | ||
super(); | ||
this.left = left; | ||
this.right = right; | ||
this.schemas = [left, right]; | ||
} | ||
// TODO If One is record and other is Object than remove object keys before parsing it as record | ||
// TODO if both are Object records we got to allowUnknown. | ||
parse(value) { | ||
for (const schema of [this.left, this.right]) { | ||
for (const schema of this.schemas) { | ||
// Todo What about unknowns keys of object intersections? | ||
@@ -232,2 +309,20 @@ if (schema instanceof ObjectType) { | ||
} | ||
else if (this.schemas.every(schema => schema instanceof RecordType)) { | ||
schema.parse(value, { allowUnknown: true }); | ||
} | ||
else if (schema instanceof RecordType && typeOf(value) === 'object') { | ||
const objectSchema = this.schemas.find(x => x instanceof ObjectType); | ||
if (!objectSchema) { | ||
schema.parse(value); | ||
} | ||
const objectKeys = objectSchema[getKeyShapesSymbol](); | ||
const proxy = Object.keys(value).reduce((acc, key) => { | ||
if (objectKeys.includes(key)) { | ||
return acc; | ||
} | ||
acc[key] = value[key]; | ||
return acc; | ||
}, {}); | ||
schema.parse(proxy); | ||
} | ||
else { | ||
@@ -240,5 +335,5 @@ schema.parse(value); | ||
} | ||
exports.string = () => new StringType(); | ||
exports.string = (opts) => new StringType(opts); | ||
exports.boolean = () => new BooleanType(); | ||
exports.number = () => new NumberType(); | ||
exports.number = (opts) => new NumberType(opts); | ||
exports.unknown = () => new UnknownType(); | ||
@@ -250,2 +345,4 @@ exports.literal = (literal) => new LiteralType(literal); | ||
exports.intersection = (l, r) => new IntersectionType(l, r); | ||
exports.record = (type) => new RecordType(type); | ||
exports.dictionary = (type) => new RecordType(exports.union([type, undefinedValue()])); | ||
const undefinedValue = () => new UndefinedType(); | ||
@@ -252,0 +349,0 @@ exports.undefined = undefinedValue; |
{ | ||
"name": "myzod", | ||
"version": "0.0.5", | ||
"version": "0.0.6", | ||
"description": "", | ||
@@ -5,0 +5,0 @@ "main": "./libs/index.js", |
Sorry, the diff of this file is not supported yet
30975
519