Comparing version 0.0.18 to 0.0.19
@@ -65,3 +65,3 @@ declare abstract class Type<T> { | ||
declare type ObjectShape = Record<string, AnyType>; | ||
declare type InferObjectShape<T> = { | ||
declare type InferObjectShape<T extends ObjectShape> = { | ||
[key in keyof T]: T[key] extends Type<infer K> ? K : any; | ||
@@ -96,2 +96,3 @@ }; | ||
private readonly opts; | ||
private readonly _parse; | ||
constructor(schema: T, opts?: ArrayOptions); | ||
@@ -104,6 +105,3 @@ parse(value: unknown, parseOptions?: PathOptions): Infer<T>[]; | ||
} | ||
declare type TupleToUnion<T extends any[]> = T[number]; | ||
declare type InferTupleUnion<T extends AnyType[]> = TupleToUnion<{ | ||
[P in keyof T]: T[P] extends Type<infer K> ? K : any; | ||
}>; | ||
declare type InferTupleUnion<T extends any[]> = Infer<T[number]>; | ||
declare type UnionOptions = { | ||
@@ -121,6 +119,6 @@ strict?: boolean; | ||
private readonly right; | ||
private readonly _parse; | ||
constructor(left: T, right: K); | ||
parse(value: unknown, opts?: PathOptions): Eval<Infer<T> & Infer<K>>; | ||
private parseObjectIntersection; | ||
private parseRecordIntersection; | ||
private parseRecordObjectIntersection; | ||
@@ -144,3 +142,3 @@ } | ||
constructor(rootSchema: T, pickedKeys: K[]); | ||
parse(value: unknown): Eval<Pick<Infer<T>, K>>; | ||
parse(value: unknown, parseOptions?: ObjectOptions): Eval<Pick<Infer<T>, K>>; | ||
} | ||
@@ -151,3 +149,3 @@ declare class OmitType<T extends AnyType, K extends keyof Infer<T>> extends Type<Omit<Infer<T>, K>> { | ||
constructor(rootSchema: T, omittedKeys: K[]); | ||
parse(value: unknown): Eval<Omit<Infer<T>, K>>; | ||
parse(value: unknown, opts?: ObjectOptions): Eval<Omit<Infer<T>, K>>; | ||
} | ||
@@ -218,3 +216,4 @@ export declare const string: (opts?: Partial<{ | ||
enum: <T_10>(e: T_10) => EnumType<T_10>; | ||
ValidationError: typeof ValidationError; | ||
}; | ||
export default _default; |
@@ -49,2 +49,5 @@ "use strict"; | ||
} | ||
const allowUnknownSymbol = Symbol.for('allowUnknown'); | ||
const requiredKeysSymbol = Symbol.for('requiredKeys'); | ||
const shapekeysSymbol = Symbol.for('shapeKeys'); | ||
class StringType extends Type { | ||
@@ -169,3 +172,2 @@ constructor(opts = {}) { | ||
} | ||
const getKeyShapesSymbol = Symbol.for('getKeyShapes'); | ||
class ObjectType extends Type { | ||
@@ -176,4 +178,12 @@ constructor(objectShape, opts) { | ||
this.opts = opts; | ||
//@ts-ignore | ||
this[getKeyShapesSymbol] = () => Object.keys(this.objectShape); | ||
const keys = Object.keys(this.objectShape); | ||
this[allowUnknownSymbol] = !!(opts === null || opts === void 0 ? void 0 : opts.allowUnknown); | ||
this[shapekeysSymbol] = keys; | ||
this[requiredKeysSymbol] = keys.filter(key => { | ||
const keySchema = this.objectShape[key]; | ||
const isUndefined = keySchema instanceof UndefinedType; | ||
const unionOfUndefined = keySchema instanceof UnionType && | ||
keySchema.schemas.some((schema) => schema instanceof UndefinedType); | ||
return !isUndefined && !unionOfUndefined; | ||
}); | ||
} | ||
@@ -265,2 +275,6 @@ parse(value, parseOpts = {}) { | ||
this.opts = opts; | ||
this._parse = | ||
this.schema instanceof ObjectType || this.schema instanceof ArrayType | ||
? (elem) => this.schema.parse(elem, { suppressPathErrMsg: true }) | ||
: (elem) => this.schema.parse(elem); | ||
} | ||
@@ -292,13 +306,8 @@ parse(value, parseOptions) { | ||
} | ||
value.forEach((elem, idx) => { | ||
for (let i = 0; i < value.length; i++) { | ||
try { | ||
if (this.schema instanceof ObjectType || this.schema instanceof ArrayType) { | ||
this.schema.parse(elem, { suppressPathErrMsg: true }); | ||
} | ||
else { | ||
this.schema.parse(elem); | ||
} | ||
this._parse(value[i]); | ||
} | ||
catch (err) { | ||
const path = err.path ? [idx, ...err.path] : [idx]; | ||
const path = err.path ? [i, ...err.path] : [i]; | ||
const msg = (parseOptions === null || parseOptions === void 0 ? void 0 : parseOptions.suppressPathErrMsg) ? err.message | ||
@@ -308,3 +317,3 @@ : `error at ${prettyPrintPath(path)} - ${err.message}`; | ||
} | ||
}); | ||
} | ||
return value; | ||
@@ -352,2 +361,3 @@ } | ||
} | ||
const isPickOrOmitType = (schema) => schema instanceof PickType || schema instanceof OmitType; | ||
class IntersectionType extends Type { | ||
@@ -358,46 +368,91 @@ constructor(left, right) { | ||
this.right = right; | ||
this[allowUnknownSymbol] = !!(this.left[allowUnknownSymbol] && this.right[allowUnknownSymbol]); | ||
if (this.left[requiredKeysSymbol] && this.right[requiredKeysSymbol]) { | ||
//@ts-ignore | ||
this[requiredKeysSymbol] = Array.from(new Set([...this.left[requiredKeysSymbol], ...this.right[requiredKeysSymbol]])); | ||
} | ||
if (this.left[shapekeysSymbol] && this.right[shapekeysSymbol]) { | ||
//@ts-ignore | ||
this[shapekeysSymbol] = Array.from(new Set([...this.left[shapekeysSymbol], ...this.right[shapekeysSymbol]])); | ||
} | ||
this._parse = (() => { | ||
if (this.left instanceof ObjectType && this.right instanceof ObjectType) { | ||
return (value, opts) => this.parseObjectIntersection(value, opts); | ||
} | ||
if (this.left instanceof RecordType && this.right instanceof RecordType) { | ||
const leftSchema = this.left.schema; | ||
const rightSchema = this.right.schema; | ||
const record = new RecordType(leftSchema.and(rightSchema)); | ||
return (value) => record.parse(value); | ||
} | ||
if (this.left instanceof RecordType && this.right instanceof ObjectType) { | ||
this[requiredKeysSymbol] = this.right[requiredKeysSymbol]; | ||
//@ts-ignore | ||
return (value) => this.parseRecordObjectIntersection(value, this.left, this.right); | ||
} | ||
if (this.right instanceof RecordType && this.left instanceof ObjectType) { | ||
this[requiredKeysSymbol] = this.left[requiredKeysSymbol]; | ||
//@ts-ignore | ||
return (value) => this.parseRecordObjectIntersection(value, this.right, this.left); | ||
} | ||
// TODO Investigate why I unwrap partials in a new intersection again | ||
if (this.left instanceof PartialType) { | ||
this[requiredKeysSymbol] = this.right[requiredKeysSymbol]; | ||
return (value) => new IntersectionType(this.left.schema, this.right).parse(value); | ||
} | ||
if (this.right instanceof PartialType) { | ||
this[requiredKeysSymbol] = this.left[requiredKeysSymbol]; | ||
return (value) => new IntersectionType(this.left, this.right.schema).parse(value); | ||
} | ||
if (isPickOrOmitType(this.left) && isPickOrOmitType(this.right)) { | ||
return (value) => { | ||
//@ts-ignore | ||
this.left.parse(value, { allowUnknown: true }); | ||
//@ts-ignore | ||
this.right.parse(value, { allowUnknown: true }); | ||
return value; | ||
}; | ||
} | ||
if (isPickOrOmitType(this.left)) { | ||
return (value) => { | ||
//@ts-ignore | ||
this.left.parse(value, { allowUnknown: true }); | ||
this.right.parse(value); | ||
return value; | ||
}; | ||
} | ||
if (isPickOrOmitType(this.right)) { | ||
return (value) => { | ||
this.left.parse(value); | ||
//@ts-ignore | ||
this.right.parse(value, { allowUnknown: true }); | ||
return value; | ||
}; | ||
} | ||
return (value) => { | ||
this.left.parse(value); | ||
this.right.parse(value); | ||
return value; | ||
}; | ||
})(); | ||
} | ||
parse(value, opts) { | ||
if (this.left instanceof ObjectType && this.right instanceof ObjectType) { | ||
return this.parseObjectIntersection(value, opts); | ||
if (!this[allowUnknownSymbol] && this[shapekeysSymbol]) { | ||
const expectedShapeKeys = this[shapekeysSymbol]; | ||
const invalidKeys = Object.keys(value).filter((key) => !expectedShapeKeys.includes(key)); | ||
if (invalidKeys.length > 0) { | ||
throw new ValidationError('unexpected keys on object ' + JSON.stringify(invalidKeys)); | ||
} | ||
} | ||
if (this.left instanceof RecordType && this.right instanceof RecordType) { | ||
return this.parseRecordIntersection(value); | ||
} | ||
if (this.left instanceof RecordType && this.right instanceof ObjectType) { | ||
return this.parseRecordObjectIntersection(value, this.left, this.right); | ||
} | ||
if (this.right instanceof RecordType && this.left instanceof ObjectType) { | ||
return this.parseRecordObjectIntersection(value, this.right, this.left); | ||
} | ||
if (this.left instanceof PartialType) { | ||
return new IntersectionType(this.left.schema, this.right).parse(value); | ||
} | ||
if (this.right instanceof PartialType) { | ||
return new IntersectionType(this.left, this.right.schema).parse(value); | ||
} | ||
this.left.parse(value); | ||
this.right.parse(value); | ||
return value; | ||
return this._parse(value, opts); | ||
} | ||
parseObjectIntersection(value, opts) { | ||
const intersectionKeys = new Set([ | ||
...this.left[getKeyShapesSymbol](), | ||
...this.right[getKeyShapesSymbol](), | ||
]); | ||
const invalidKeys = Object.keys(value).filter(key => !intersectionKeys.has(key)); | ||
if (invalidKeys.length > 0) { | ||
throw new ValidationError('unexpected keys on object ' + JSON.stringify(invalidKeys)); | ||
} | ||
const parsingOptions = Object.assign(Object.assign({}, opts), { allowUnknown: true }); | ||
return Object.assign(Object.assign({}, this.left.parse(value, parsingOptions)), this.right.parse(value, parsingOptions)); | ||
const parsingOptions = { suppressPathErrMsg: opts === null || opts === void 0 ? void 0 : opts.suppressPathErrMsg, allowUnknown: true }; | ||
this.left.parse(value, parsingOptions); | ||
this.right.parse(value, parsingOptions); | ||
return value; | ||
} | ||
parseRecordIntersection(value) { | ||
const leftSchema = this.left.schema; | ||
const rightSchema = this.right.schema; | ||
return new RecordType(leftSchema.and(rightSchema)).parse(value); | ||
} | ||
parseRecordObjectIntersection(value, recordSchema, objectSchema) { | ||
objectSchema.parse(value, { allowUnknown: true }); | ||
const objectKeys = objectSchema[getKeyShapesSymbol](); | ||
const objectKeys = objectSchema[shapekeysSymbol]; | ||
const proxy = Object.keys(value).reduce((acc, key) => { | ||
@@ -509,16 +564,22 @@ if (!objectKeys.includes(key)) { | ||
this.schema = createPickedSchema(rootSchema, pickedKeys); | ||
const rootShapeKeys = rootSchema[shapekeysSymbol]; | ||
this[shapekeysSymbol] = rootShapeKeys && rootShapeKeys.filter((key) => pickedKeys.includes(key)); | ||
} | ||
parse(value) { | ||
parse(value, parseOptions) { | ||
if (value === null || typeof value !== 'object') { | ||
throw new ValidationError('expected type to be object but got ' + typeOf(value)); | ||
} | ||
const keys = Object.keys(value); | ||
const illegalKeys = keys.filter(key => !this.pickedKeys.includes(key)); | ||
if (illegalKeys.length > 0) { | ||
throw new ValidationError('unexpected keys on object: ' + JSON.stringify(illegalKeys)); | ||
if (!(parseOptions === null || parseOptions === void 0 ? void 0 : parseOptions.allowUnknown)) { | ||
const keys = Object.keys(value); | ||
const illegalKeys = keys.filter(key => !this.pickedKeys.includes(key)); | ||
if (illegalKeys.length > 0) { | ||
throw new ValidationError('unexpected keys on object: ' + JSON.stringify(illegalKeys)); | ||
} | ||
} | ||
// For records if the key isn't present the record won't be able to validate it. | ||
for (const key of this.pickedKeys) { | ||
if (!value.hasOwnProperty(key)) { | ||
value[key] = undefined; | ||
if (this.schema instanceof RecordType) { | ||
for (const key of this.pickedKeys) { | ||
if (!value.hasOwnProperty(key)) { | ||
value[key] = undefined; | ||
} | ||
} | ||
@@ -566,11 +627,15 @@ } | ||
this.schema = createOmittedSchema(rootSchema, omittedKeys); | ||
const rootShapeKeys = rootSchema[shapekeysSymbol]; | ||
this[shapekeysSymbol] = rootShapeKeys && rootShapeKeys.filter((key) => !omittedKeys.includes(key)); | ||
} | ||
parse(value) { | ||
parse(value, opts) { | ||
if (value === null || typeof value !== 'object') { | ||
throw new ValidationError('expected type to be object but got ' + typeOf(value)); | ||
} | ||
const keys = Object.keys(value); | ||
const illegalKeys = keys.filter(key => this.omittedKeys.includes(key)); | ||
if (illegalKeys.length > 0) { | ||
throw new ValidationError('unexpected keys on object: ' + JSON.stringify(illegalKeys)); | ||
if (!(opts === null || opts === void 0 ? void 0 : opts.allowUnknown)) { | ||
const keys = Object.keys(value); | ||
const illegalKeys = keys.filter(key => this.omittedKeys.includes(key)); | ||
if (illegalKeys.length > 0) { | ||
throw new ValidationError('unexpected keys on object: ' + JSON.stringify(illegalKeys)); | ||
} | ||
} | ||
@@ -619,2 +684,3 @@ return this.schema.parse(value); | ||
enum: enumValue, | ||
ValidationError, | ||
}; |
{ | ||
"name": "myzod", | ||
"version": "0.0.18", | ||
"version": "0.0.19", | ||
"description": "", | ||
@@ -10,3 +10,4 @@ "main": "./libs/index.js", | ||
"build": "rm -rf ./libs && tsc", | ||
"pub": "npm t && npm run build && npm publish" | ||
"pub": "npm t && npm run build && npm publish", | ||
"bench": "ts-node test/benchmark.ts > test/results.json" | ||
}, | ||
@@ -13,0 +14,0 @@ "keywords": [], |
48811
909