MCPcopy Index your code
hub / github.com/davidmdm/myzod

github.com/davidmdm/myzod @v1.2.2

Chat with this repo
repository ↗ · DeepWiki ↗ · release v1.2.2 ↗ · + Follow
185 symbols 319 edges 6 files 0 documented · 0% 1 cross-repo links updated 17mo ago★ 3305 open issues
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

myzod

Schema Validation with typescript type inference.

Acknowledgements

Major Shout-out to zod for the inspiration.

Description

Myzod tries to emulate the typescript type system as much as possible and is even in some ways a little stricter. The goal is that writing a schema feels the same as defining a typescript type, with equivalent & and | operators, and well known Generic types like Record, Pick and Omit. On top of that myzod aims to offer validation within the schemas for such things as number ranges, string patterns and lengths to help enforce business logic.

The resulting package has a similar api to zod with a little bit of inspiration from joi.

The goal is to write schemas from which the type of a successfully parsed value can be inferred. With myzod typescript types and validation logic no longer need to be maintained separately.

Performance

When parsing equivalent simple object (with nesting) schemas for myzod, zod and joi, on my machine Linux Ubuntu 18.04 running NodeJS 13.X, the results are as such:

objects parsed per second:

  • zod: 51861
  • joi: 194325
  • myzod: 1288659

myzod vs zod: ~25 X Speedup

myzod vs joi: ~6 X Speedup

Installation

npm install --save myzod

Usage

Myzod is used by creating a schema, extracting the type by inferring it, and finally by parsing javascript values.

import myzod, { Infer } from 'myzod';

const personSchema = myzod.object({
  id: myzod.number(),
  name: myzod.string().pattern(/^[A-Z]/),
  age: myzod.number().min(0),
  birthdate: myzod.number().or(myzod.string()),
  employed: myzod.boolean(),
  friendIds: myzod.array(myzod.number()).nullable()
});

type Person = Infer<typeof personSchema>;

const person: Person = personSchema.parse({ ... });

Api Reference

Type Root

Primitive Types

Reference Types

Logical Types

Recursive Schemas

Type.

All myzod schemas extend the generic myzod.Type class, and as such inherit these methods:

Type.parse

Takes an unknown value, and returns it typed if passed validation. Otherwise throws a myzod.ValidationError

parse(value: unknown): T

Type.try

Takes an unknown value and returns a result which will either be the parsed value or an instance of ValidationError. This api is useful if you do not want to throw exceptions.

const result = schema.try(data);
if (result instanceof myzod.ValidationError) {
  // handle Error
} else {
  // result is of type: myzod.Infer<typeof schema>
}
Type.and

Shorthand for creating intersection types of two schemas.

const nameSchema = myzod.object({ name: myzod.string() });
const ageSchema = myzod.object({ age: myzod.number() });

const personSchema = nameSchema.and(ageSchema); // Same as ageSchema.and(nameSchema);

type Person = Infer<typeof personSchema>; // => { name: string; age: number; }
Type.or

Shorthand for creating union types of two schemas.

const stringOrBoolSchema = myzod.string().or(myzod.boolean());

type StringOrUndefined = Infer<typeof stringOrBoolSchema>; // => string | boolean
Type.optional

Returns a new schema which is a wrapped OptionalType of the current schema.

const optionalStringSchema = myzod.string().optional(); // => OptionalType<StringType>

type StringOrUndefined = Infer<typeof optionalStringSchema>; // => string | undefined
Type.nullable

Returns a new schema which is a wrapped NullableType of the current schema.

const nullableStringSchema = myzod.string().nullable(); // => NullableType<StringType>

type StringOrUndefined = Infer<typeof nullableStringSchema>; // => string | null
Type.map

Returns a new generic schema to the mapped type. Useful for transforming validated input into a new type on parse.

const ObjectIDSchema = myzod
                         .string()
                         .withPredicate(ObjectId.isValid, 'must be an object ID')
                         .map(value => new ObjectId(value));

// Infer<ObjectIDSchema> === ObjectId

const id = ObjectIDSchema.parse('507c7f79bcf86cd7994f6c0e');
id instanceof ObjectId; // true

const id2 = ObjectIDSchema.parse('some string'); // Throws VaidationError with message "must be an object ID"'

String

options:

  • min number - min length of string
  • max number - max length of string
  • pattern RegExp - regular expression string must match
  • valid string[] - list of valid stings
  • predicate Predicate<string> - custom predicates to apply to string value

methods:

  • min(value: number, errMsg?: string) => StringType
    returns a new string schema where minimum string lenth is min
  • max(value: number, errMsg?: string) => StringType
    returns a new string schema where maximum string length is max
  • pattern(value: RegExp, errMsg?: string) => StringType
    returns a new string schema where string must match pattern
  • valid(list: string[], errMsg?: string) => StringType
    returns a new string schema where string must be included in valid string array
  • withPredicate(fn: (val: string) => boolean), errMsg?: string }
    returns a new schema where string must pass predicate function(s).
  • default(value: string | (() => string)) => StringType
    returns a new schema which will use defaultValue when parsing undefined

options can be passed as an option object or chained from schema.

myzod.string({ min: 3, max: 10, pattern: /^hey/ });
// same as
myzod.string().min(3).max(10).pattern(/^hey/);

The valid options lets you validate against a set of strings.

const helloworld = myzod.string().valid(['hello', 'world']);
typeof HelloWorld = myzod.Infer<typeof helloworld>; // => string

if however you want the stings to be typed used the literals helper function:

const helloworld = myzod.literals('hello', 'world');
type HelloWorld = myzod.Infer<typeof helloworld>; // => 'hello' | 'world'

Myzod is not interested in reimplementing all possible string validations, ie isUUID, isEmail, isAlphaNumeric, etc. The myzod string validation can be easily extended via the withPredicate API.

const uuidSchema = myzod.string().withPredicate(validator.isUUID, 'expected string to be uuid');

type UUID = Infer<typeof uuidSchema>; // => string

uuidSchema.parse('hello world'); // Throws ValidationError with message 'expected string to be uuid'
// note that if predicate function throws an error that message will be used instead

Note that you can register multiple predicates, and that each invocation will create a new schema:

const greeting = myzod.string().withPredicate(value => value.startsWith('hello'), 'string must start with hello');
const evenGreeting = greeting.withPredicate(value => value.length % 2 === 0, 'string must have even length');
const oddGreeting = greeting.withPredicate(value => value.length % 2 === 1, 'string must have odd length');

You can use default values or functions to create default values for myzod string schemas.

const uuidSchema = z.string().default(() => uuidv4());
const val = uuidSchema.parse(undefined); // val is a valid uuid constructed by uuidv4()
uuid.parse(null); // throws an error

Number

options:

  • min: number - min value for number
  • max: number - max value for number
  • coerce: boolean - when true will attempt to coerce strings to numbers. default false

methods:

  • min(value: number, errMsg?: string) => NumberType
    returns a new number schema where number must be greater than or equal to min value
  • max(value: number, errMsg?: string) => NumberType
    returns a new number schema where number must be less than or equal to max value
  • withPredicate(fn: (value: number) => boolean, errMsg?: string) => NumberType
    returns a new number schema where number must satisfy predicate function
  • coerce(flag?: boolean) => NumberType
    returns a new number schema which depending on the flag will coerce strings to numbers
  • default(value: number | (() => number)) => NumberType
    returns a new number schema which will use value as default when parsing undefined

options can be passed as an option object or chained from schema.

myzod.number({ min: 0, max: 10 });
// Same as:
myzod.number().min(0).max(10);

Coercion example:

const schema = myzod.number().coerce(); // same as myzod.number({ coerce: true });

const value = schema.parse('42');

assert.ok(typeof value === 'number'); // succeeds
assert.equal(value, 42); // succeeds

BigInt

options:

  • min: number - min value for number
  • max: number - max value for number

methods:

  • min(value: number | bigint) => BigIntType
    returns a new bigint schema where value must be at least min
  • max(value: number | bigint) => BigIntType
    returns a new bigint schema where value must be lesser or equal to max
  • withPredicate(fn: (value: bigint) => boolean, errMsg?: string) => BigIntType
    returns a new bigint schema where value must pass predicate function

options can be passed as an option object or chained from schema.

myzod.bigint({ min: 0, max: 10 });
// Same as:
myzod.bigint().min(0).max(10);

const integer = myzod.bigint();
type Integer = myzod.Infer<typeof integer>; // => bigint

The bigint schema automatically coerces bigint interpretable numbers and strings into bigint values.

const schema = myzod.bigint();
const value = schema.parse('42');

assert.ok(typeof value === 'bigint'); // succeeds
assert.equal(value, 42n); // succeeds

Boolean

methods:

  • default(value: boolean | (() => boolean)) => BooleanType
    returns a new boolean schema instance which will use value as default when parsing undefined
myzod.boolean();

Undefined

myzod.undefined();

Null

methods:

  • default() => NullType
    returns a new null schema instance which will use set null as default when parsing undefined
myzod.null();

Literal

methods:

  • default() => LiteralType
    returns a new literal schema instance which will use its literal as default when parsing undefined

Just as in typescript we can type things using literals

const schema = myzod.literal('Value');
type Val = Infer<typeof schema>; // => 'Value'

Sometimes we do not want to go all out and create an enum to represent a combination of literals. Myzod offers a utility function to avoid have to "or" multiple times over many literalTypes.

Literals
const schema = myzod.literals('red', 'green', 'blue');
type Schema = myzod.Infer<typeof schema>; // => 'red' | 'green' | 'blue'

// Other equivalent ways of creating the same schema:
const schema = myzod.literal('red').or(myzod.literal('green')).or(myzod.literal('blue'));
const schema = myzod.union([myzod.literal('red'), myzod.literal('green'), myzod.literal('blue')]);

Unknown

methods:

  • default(value: any | (() => any)) => UnknownType
    returns a new unknown schema instance which will use value as default when parsing undefined
myzod.unknown();

The unknown schema does nothing when parsing by itself. However it is useful to require a key to be present inside an object schema when we don't know or don't care about the type.

const schema = myzod.object({ unknownYetRequiredField: myzod.unknown() });
type Schema = Infer<typeof schema>; // => { unknownYetRequiredField: unknown }

schema.parse({}); // throws a ValidationError
schema.parse({ unknownYetRequiredField: 'hello' }); // succeeds

Object

options:

  • allowUnknown: boolean - allows for object with keys not specified in expected shape to succeed parsing, default false
  • suppressErrPathMsg: boolean - suppress the path to the invalid key in thrown validationErrors. This option should stay false for most cases but is used internally to generate appropriate messages when validating nested objects. default false

myzod.object is the way to construct arbitrary object schemas.

function object(shape: { [key: string]: Type<T> }, opts?: options);

examples:

const strictEmptyObjSchema = myzod.object({});
const emptyObjSchema = myzod.object({}, { allowUnknown: true });

// Both Schemas infer the same type
type Empty = Infer<typeof emptyObjSchema>; // => {}
type StrictEmpty = Infer<typeof strictEmptyObjSchema>; // => {}

emptyObjSchema.parse({ key: 'value' }); // => succeeds
strictEmptyObjSchema.parse({ key: 'value' }); // => throws ValidationError because not expected key: "key"

const personSchema = myzod.object({
  name: myzod.string(),
});
const shape = personSchema.shape(); // => returns { name: myzod.string() }

object.withPredicate

You can add predicate functions to object schemas. Note that these predicate functions will not be kept around for schemas produces from object.pick/omit/partial as they predicate function signatures need to change for those signatures.

```typescript const registrationSchema = myzod .object({ email: z.string().withPredicate(validator.isEmail, 'expected email'), password: z.string(

Extension points exported contracts — how you extend this code

WithPredicate (Interface)
(no doc) [7 implementers]
src/types.ts
Defaultable (Interface)
(no doc) [14 implementers]
src/types.ts

Core symbols most depended-on inside this repo

parse
called by 171
src/types.ts
and
called by 63
src/types.ts
pick
called by 50
src/types.ts
default
called by 30
src/types.ts
omit
called by 28
src/types.ts
partial
called by 27
src/types.ts
withPredicate
called by 19
src/types.ts
min
called by 11
src/types.ts

Shape

Method 100
Class 41
Function 39
Enum 3
Interface 2

Languages

TypeScript100%

Modules by API surface

src/types.ts157 symbols
src/index.ts22 symbols
test/parsing.test.ts4 symbols
test/types.test.ts2 symbols

Used by 1 indexed graphs manifest dependencies, hub-wide

For agents

$ claude mcp add myzod \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact

Ask about this repo answers extend the page