{"id":14156653,"url":"https://github.com/JacobWeisenburger/zod_utilz","last_synced_at":"2025-08-06T03:31:00.158Z","repository":{"id":65241534,"uuid":"588680080","full_name":"JacobWeisenburger/zod_utilz","owner":"JacobWeisenburger","description":"Framework agnostic utilities for Zod","archived":false,"fork":false,"pushed_at":"2024-06-14T23:09:58.000Z","size":239,"stargazers_count":169,"open_issues_count":3,"forks_count":9,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-12-07T16:02:05.546Z","etag":null,"topics":["typescript","zod"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JacobWeisenburger.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"JacobWeisenburger"}},"created_at":"2023-01-13T18:09:39.000Z","updated_at":"2024-12-05T21:38:05.000Z","dependencies_parsed_at":"2024-01-16T22:21:11.752Z","dependency_job_id":"d5fce22d-6abb-439f-9290-b050371ca7d2","html_url":"https://github.com/JacobWeisenburger/zod_utilz","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobWeisenburger%2Fzod_utilz","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobWeisenburger%2Fzod_utilz/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobWeisenburger%2Fzod_utilz/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JacobWeisenburger%2Fzod_utilz/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JacobWeisenburger","download_url":"https://codeload.github.com/JacobWeisenburger/zod_utilz/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228651884,"owners_count":17951894,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["typescript","zod"],"created_at":"2024-08-17T08:07:33.674Z","updated_at":"2024-12-09T04:30:45.112Z","avatar_url":"https://github.com/JacobWeisenburger.png","language":"TypeScript","funding_links":["https://github.com/sponsors/JacobWeisenburger"],"categories":["typescript"],"sub_categories":[],"readme":"\u003cdiv align='center'\u003e\r\n    \u003cimg src='logo.svg' width='200px' alt='Zod Utilz logo' /\u003e\r\n    \u003ch1\u003eZod Utilz\u003c/h1\u003e\r\n    \u003ch3\u003e\r\n        Framework agnostic utilities for\r\n        \u003ca href='https://github.com/colinhacks/zod' rel='nofollow'\u003e\r\n            Zod\r\n        \u003c/a\u003e\r\n    \u003c/h3\u003e\r\n\u003c/div\u003e\r\n\r\n\u003cdiv align='center'\u003e\r\n    \u003ca href='https://github.com/JacobWeisenburger' rel='nofollow'\u003e\r\n        \u003cimg alt='Created by Jacob Weisenburger'\r\n            src='https://img.shields.io/badge/created%20by-Jacob%20Weisenburger-274D82.svg'\u003e\r\n    \u003c/a\u003e\r\n    \u003ca href='https://github.com/JacobWeisenburger/zod_utilz/stargazers' rel='nofollow'\u003e\r\n        \u003cimg alt='stars' src='https://img.shields.io/github/stars/weis-guys/result?color=blue'\u003e\r\n    \u003c/a\u003e\r\n\u003c/div\u003e\r\n\r\n\u003cdiv align='center'\u003e\r\n    \u003ca href='https://www.npmjs.com/package/zod_utilz' rel='nofollow'\u003e\r\n        \u003cimg alt='npm' src='https://img.shields.io/npm/v/zod_utilz?color=blue'\u003e\r\n    \u003c/a\u003e\r\n    \u003ca href='https://www.npmjs.com/package/zod_utilz' rel='nofollow'\u003e\r\n        \u003cimg alt='downloads' src='https://img.shields.io/npm/dw/zod_utilz?color=blue'\u003e\r\n    \u003c/a\u003e\r\n\u003c/div\u003e\r\n\r\n\u003cdiv align=\"center\"\u003e\r\n    \u003ca href=\"https://github.com/JacobWeisenburger/zod_utilz#zod-utilz\"\u003eDocs\u003c/a\u003e\r\n    \u003cspan\u003e\u0026nbsp;\u0026nbsp;•\u0026nbsp;\u0026nbsp;\u003c/span\u003e\r\n    \u003ca href=\"https://github.com/JacobWeisenburger/zod_utilz\"\u003egithub\u003c/a\u003e\r\n    \u003cspan\u003e\u0026nbsp;\u0026nbsp;•\u0026nbsp;\u0026nbsp;\u003c/span\u003e\r\n    \u003ca href=\"https://www.npmjs.com/package/zod_utilz\"\u003enpm\u003c/a\u003e\r\n\u003c/div\u003e\r\n\r\n\u003c!-- Dist Readme Stops Here --\u003e\r\n\r\n\u003cbr /\u003e\r\n\r\n## Table of contents\r\n- [Purpose](#purpose)\r\n- [Contribute](#contribute)\r\n- [Yet another library](#yet-another-library)\r\n- [Installation](#installation)\r\n    - [From npm (Node/Bun)](#from-npm-nodebun)\r\n- [Getting Started](#getting-started)\r\n    - [import](#import)\r\n- [Utilz](#utilz)\r\n    - [SPR (SafeParseResult)](#spr)\r\n    - [makeErrorMap](#makeerrormap)\r\n    - [useTypedParsers](#usetypedparsers)\r\n    - [coerce](#coerce)\r\n    - [useURLSearchParams](#useurlsearchparams)\r\n    - [useFormData](#useformdata)\r\n    - [partialSafeParse](#partialsafeparse)\r\n    - [json](#json)\r\n    - [stringToJSON](#stringtojson)\r\n- [TODO](#todo)\r\n\r\n## Purpose\r\n- Simplify common tasks in [Zod](https://github.com/colinhacks/zod)\r\n- Fill the gap of features that might be missing in [Zod](https://github.com/colinhacks/zod)\r\n- Provide implementations for potential new features in [Zod](https://github.com/colinhacks/zod)\r\n\r\n## Contribute\r\nAlways open to ideas. Positive or negative, all are welcome. Feel free to contribute an [issue](https://github.com/JacobWeisenburger/zod_utilz/issues) or [PR](https://github.com/JacobWeisenburger/zod_utilz/pulls).\r\n\r\n## Yet another library\r\nYou might not want to install yet another library only to get access to that one [Util](#utilz) you need. No worries. Feel free to copy and paste the code you need into your project. It won't get updated when this library gets updated, but it will reduce your bundle size. :D\r\n\r\nPerhaps in the future there will be a way to install only the [Utilz](#utilz) you need. If you know how to do this, please [let me know](https://github.com/JacobWeisenburger/zod_utilz/issues).\r\n\r\n## Installation\r\n\r\n### From npm (Node/Bun)\r\n```sh\r\nnpm install zod_utilz\r\nyarn add zod_utilz\r\npnpm add zod_utilz\r\nbun add zod_utilz\r\n```\r\n\r\n## Getting Started\r\n\r\n### import\r\n#### [Node/Bun](https://www.npmjs.com/package/zod_utilz)\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\n```\r\n\r\n#### [Deno](https://deno.land/x/zod_utilz)\r\n```ts\r\nimport { zu } from 'npm:zod_utilz'\r\n```\r\n\r\n## Utilz\r\n\r\n### SPR\r\nSPR stands for SafeParseResult\r\n\r\nThis enables [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) or [nullish coalescing](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing) for `z.SafeParseReturnType`.\r\n\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst schema = z.object( { foo: z.string() } )\r\nconst result = zu.SPR( schema.safeParse( { foo: 42 } ) )\r\nconst fooDataOrErrors = result.data?.foo ?? result.error?.format().foo?._errors\r\n```\r\n\r\n### makeErrorMap\r\nSimplifies the process of making a `ZodErrorMap`\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\n\r\nconst errorMap = zu.makeErrorMap( {\r\n    required: 'Custom required message',\r\n    invalid_type: ( { data } ) =\u003e `${ data } is an invalid type`,\r\n    too_big: ( { maximum } ) =\u003e `Maximum length is ${ maximum }`,\r\n    invalid_enum_value: ( { data, options } ) =\u003e\r\n        `${ data } is not a valid enum value. Valid options: ${ options?.join( ' | ' ) } `,\r\n} )\r\n\r\nconst stringSchema = z.string( { errorMap } ).max( 32 )\r\n\r\nzu.SPR( stringSchema.safeParse( undefined ) ).error?.issues[ 0 ].message\r\n// Custom required message\r\n\r\nzu.SPR( stringSchema.safeParse( 42 ) ).error?.issues[ 0 ].message\r\n// 42 is an invalid type\r\n\r\nzu.SPR( stringSchema.safeParse( 'this string is over the maximum length' ) ).error?.issues[ 0 ].message\r\n// Maximum length is 32\r\n\r\nconst enumSchema = z.enum( [ 'foo', 'bar' ], { errorMap } )\r\n\r\nzu.SPR( enumSchema.safeParse( 'baz' ) ).error?.issues[ 0 ].message\r\n// baz is not a valid enum value. Valid options: foo | bar\r\n```\r\n\r\n### useTypedParsers\r\nEnables compile time type checking for zod parsers.\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst schemaWithTypedParsers = zu.useTypedParsers( z.literal( 'foo' ) )\r\n\r\nschemaWithTypedParsers.parse( 'foo' )\r\n// no ts errors\r\n\r\nschemaWithTypedParsers.parse( 'bar' )\r\n//                            ^^^^^\r\n// Argument of type '\"bar\"' is not assignable to parameter of type '\"foo\"'\r\n```\r\n\r\n### coerce\r\nCoercion that treats errors like normal zod errors. Prevents throwing errors when using `safeParse`.\r\n\r\n#### z.bigint()\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst bigintSchema = zu.coerce( z.bigint() )\r\nbigintSchema.parse( '42' ) // 42n\r\nbigintSchema.parse( '42n' ) // 42n\r\nzu.SPR( bigintSchema.safeParse( 'foo' ) ).error?.issues[ 0 ].message\r\n// 'Expected bigint, received string'\r\n```\r\n\r\n#### z.boolean()\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst booleanSchema = zu.coerce( z.boolean() )\r\n\r\n// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean\r\n// only exception to normal boolean coercion rules\r\nbooleanSchema.parse( 'false' ) // false\r\n\r\n// https://developer.mozilla.org/en-US/docs/Glossary/Falsy\r\n// falsy =\u003e false\r\nbooleanSchema.parse( false ) // false\r\nbooleanSchema.parse( 0 ) // false\r\nbooleanSchema.parse( -0 ) // false\r\nbooleanSchema.parse( 0n ) // false\r\nbooleanSchema.parse( '' ) // false\r\nbooleanSchema.parse( null ) // false\r\nbooleanSchema.parse( undefined ) // false\r\nbooleanSchema.parse( NaN ) // false\r\n\r\n// truthy =\u003e true\r\nbooleanSchema.parse( 'foo' ) // true\r\nbooleanSchema.parse( 42 ) // true\r\nbooleanSchema.parse( [] ) // true\r\nbooleanSchema.parse( {} ) // true\r\n```\r\n\r\n#### z.number().array()\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst numberArraySchema = zu.coerce( z.number().array() )\r\n\r\n// if the value is not an array, it is coerced to an array with one coerced item\r\nnumberArraySchema.parse( 42 ) // [ 42 ]\r\nnumberArraySchema.parse( '42' ) // [ 42 ]\r\n\r\n// if the value is an array, it coerces each item in the array\r\nnumberArraySchema.parse( [] ) // []\r\nnumberArraySchema.parse( [ '42', 42 ] ) // [ 42, 42 ]\r\n\r\nzu.SPR( numberArraySchema.safeParse( 'foo' ) ).error?.issues[ 0 ].message\r\n// 'Expected number, received nan'\r\n```\r\n\r\n### useURLSearchParams\r\nA way to parse URLSearchParams\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst schema = zu.useURLSearchParams(\r\n    z.object( {\r\n        string: z.string(),\r\n        number: z.number(),\r\n        boolean: z.boolean(),\r\n    } )\r\n)\r\n\r\nzu.SPR( schema.safeParse(\r\n    new URLSearchParams( {\r\n        string: 'foo',\r\n        number: '42',\r\n        boolean: 'false',\r\n    } )\r\n) ).data\r\n// { string: 'foo', number: 42, boolean: false }\r\n\r\nzu.SPR( schema.safeParse(\r\n    new URLSearchParams( {\r\n        string: '42',\r\n        number: 'false',\r\n        boolean: 'foo',\r\n    } )\r\n) ).error?.flatten().fieldErrors\r\n// {\r\n//     string: [ 'Expected string, received number' ],\r\n//     number: [ 'Expected number, received boolean' ],\r\n//     boolean: [ 'Expected boolean, received string' ],\r\n// }\r\n```\r\n\r\n### useFormData\r\nA way to parse FormData\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst schema = zu.useFormData(\r\n    z.object( {\r\n        string: z.string(),\r\n        number: z.number(),\r\n        boolean: z.boolean(),\r\n        file: z.instanceof( File ),\r\n    } )\r\n)\r\n```\r\n```ts\r\nconst formData = new FormData()\r\nformData.append( 'string', 'foo' )\r\nformData.append( 'number', '42' )\r\nformData.append( 'boolean', 'false' )\r\nformData.append( 'file', new File( [], 'filename.ext' ) )\r\n\r\nzu.SPR( schema.safeParse( formData ) ).data,\r\n// { string: 'foo', number: 42, boolean: false, file: File }\r\n```\r\n```ts\r\nconst formData = new FormData()\r\nformData.append( 'string', '42' )\r\nformData.append( 'number', 'false' )\r\nformData.append( 'boolean', 'foo' )\r\nformData.append( 'file', 'filename.ext' )\r\n\r\nzu.SPR( schema.safeParse( formData ) ).error?.flatten().fieldErrors,\r\n// {\r\n//     string: [ 'Expected string, received number' ],\r\n//     number: [ 'Expected number, received boolean' ],\r\n//     boolean: [ 'Expected boolean, received string' ],\r\n//     file: [ 'Input not instance of File' ],\r\n// }\r\n```\r\n\r\n### partialSafeParse\r\npartialSafeParse allows you to get the valid fields even if there was an error in another field\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst userSchema = z.object( { name: z.string(), age: z.number() } )\r\nconst result = zu.partialSafeParse( userSchema, { name: null, age: 42 } )\r\n// {\r\n//     successType: 'partial',\r\n//     validData: { age: 42 },\r\n//     invalidData: { name: null },\r\n// }\r\nresult.error?.flatten().fieldErrors\r\n// { name: [ 'Expected string, received null' ] }\r\n```\r\n\r\n### json\r\nzu.json() is a schema that validates that a JavaScript object is JSON-compatible. This includes `string`, `number`, `boolean`, and `null`, plus `Array`s and `Object`s containing JSON-compatible types as values\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst schema = zu.json()\r\nschema.parse( false ) // false\r\nschema.parse( 8675309 ) // 8675309\r\nschema.parse( { a: 'deeply', nested: [ 'JSON', 'object' ] } )\r\n// { a: 'deeply', nested: [ 'JSON', 'object' ] }\r\n```\r\n\r\n### stringToJSON\r\nzu.stringToJSON() is a schema that validates JSON encoded as a string, then returns the parsed value\r\n```ts\r\nimport { zu } from 'zod_utilz'\r\nconst schema = zu.stringToJSON()\r\nschema.parse( 'true' ) // true\r\nschema.parse( 'null' ) // null\r\nschema.parse( '[\"one\", \"two\", \"three\"]' ) // ['one', 'two', 'three']\r\nschema.parse( '\u003chtml\u003enot a JSON string\u003c/html\u003e' ) // throws\r\n```\r\n\r\n## TODO\r\nAlways open to ideas. Positive or negative, all are welcome. Feel free to contribute an [issue](https://github.com/JacobWeisenburger/zod_utilz/issues) or [PR](https://github.com/JacobWeisenburger/zod_utilz/pulls).\r\n- tests for `lib/mapValues`\r\n- Shrink Bundle Size\r\n    - tree-shaking deps\r\n        - lodash\r\n- zu.coerce\r\n    - z.date()\r\n    - z.object()\r\n        - recursively coerce props\r\n        - https://github.com/colinhacks/zod/discussions/1910\r\n- enum pick/omit\r\n    - https://github.com/colinhacks/zod/discussions/1922\r\n- BaseType (Recursively get the base type of a Zod type)\r\n  - zu.baseType( z.string() ) =\u003e z.string()\r\n  - zu.baseType( z.string().optional() ) =\u003e z.string()\r\n  - zu.baseType( z.string().optional().refine() ) =\u003e z.string()\r\n  - zu.baseType( z.string().array().optional().refine() ) =\u003e z.string().array()\r\n- Make process for minifying\r\n- GitHub Actions\r\n    - Auto publish to npm","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJacobWeisenburger%2Fzod_utilz","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FJacobWeisenburger%2Fzod_utilz","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FJacobWeisenburger%2Fzod_utilz/lists"}