Skip to content

Commit

Permalink
respects optionality of object keys without considering all undefined…
Browse files Browse the repository at this point in the history
… values as optional attributes
  • Loading branch information
BurtonSickler committed Oct 24, 2024
1 parent 350e998 commit 1d0258d
Show file tree
Hide file tree
Showing 7 changed files with 56 additions and 44 deletions.
4 changes: 4 additions & 0 deletions deno/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,7 @@ There are a growing number of tools that are built atop or support Zod natively!
- [`sveltekit-superforms`](https://github.com/ciscoheat/sveltekit-superforms): Supercharged form library for SvelteKit with Zod validation.
- [`mobx-zod-form`](https://github.com/MonoidDev/mobx-zod-form): Data-first form builder based on MobX & Zod.
- [`@vee-validate/zod`](https://github.com/logaretm/vee-validate/tree/main/packages/zod): Form library for Vue.js with Zod schema validation.
- [`zod-form-renderer`](https://github.com/thepeaklab/zod-form-renderer): Auto-infer form fields from zod schema and render them with react-hook-form with E2E type safety.

#### Zod to X

Expand All @@ -505,6 +506,7 @@ There are a growing number of tools that are built atop or support Zod natively!
- [`zod-openapi`](https://github.com/samchungy/zod-openapi): Create full OpenAPI v3.x documentation from Zod schemas.
- [`fastify-zod-openapi`](https://github.com/samchungy/fastify-zod-openapi): Fastify type provider, validation, serialization and @fastify/swagger support for Zod schemas.
- [`typeschema`](https://typeschema.com/): Universal adapter for schema validation.
- [`zodex`](https://github.com/commonbaseapp/zodex): (De)serialization for zod schemas

#### X to Zod

Expand Down Expand Up @@ -538,11 +540,13 @@ There are a growing number of tools that are built atop or support Zod natively!

- [`freerstore`](https://github.com/JacobWeisenburger/freerstore): Firestore cost optimizer.
- [`slonik`](https://github.com/gajus/slonik/tree/gajus/add-zod-validation-backwards-compatible#runtime-validation-and-static-type-inference): Node.js Postgres client with strong Zod integration.
- [`schemql`](https://github.com/a2lix/schemql): Enhances your SQL workflow by combining raw SQL with targeted type safety and schema validation.
- [`soly`](https://github.com/mdbetancourt/soly): Create CLI applications with zod.
- [`pastel`](https://github.com/vadimdemedes/pastel): Create CLI applications with react, zod, and ink.
- [`zod-xlsx`](https://github.com/sidwebworks/zod-xlsx): A xlsx based resource validator using Zod schemas.
- [`znv`](https://github.com/lostfictions/znv): Type-safe environment parsing and validation for Node.js with Zod schemas.
- [`zod-config`](https://github.com/alexmarqs/zod-config): Load configurations across multiple sources with flexible adapters, ensuring type safety with Zod.
- [`unplugin-environment`](https://github.com/r17x/js/tree/main/packages/unplugin-environment#readme): A plugin for loading enviroment variables safely with schema validation, simple with virtual module, type-safe with intellisense, and better DX 🔥 🚀 👷. Powered by Zod.

#### Utilities for Zod

Expand Down
4 changes: 2 additions & 2 deletions deno/lib/__tests__/object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -290,9 +290,9 @@ test("inferred type for unknown/any keys", () => {
myType,
{
anyOptional?: any;
anyRequired?: any;
anyRequired: any;
unknownOptional?: unknown;
unknownRequired?: unknown;
unknownRequired: unknown;
}
>(true);
});
Expand Down
30 changes: 22 additions & 8 deletions deno/lib/helpers/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ZodDefault, ZodOptional, ZodRawShape, ZodTypeAny } from "../types.ts";

export namespace util {
type AssertEqual<T, U> = (<V>() => V extends T ? 1 : 2) extends <
V
Expand Down Expand Up @@ -101,17 +103,29 @@ export namespace objectUtil {
[k in Exclude<keyof U, keyof V>]: U[k];
} & V;

type optionalKeys<T extends object> = {
[k in keyof T]: {} extends Pick<T, k> ? k : never;
type ObjectField = '_output' | '_input';

type optionalKeys<T extends ZodRawShape, I extends ObjectField> = {
[k in keyof T]: T[k] extends ZodOptional<ZodTypeAny>
? k
: T[k] extends ZodDefault<ZodTypeAny>
? I extends '_output' ? never : k
: never;
}[keyof T];
type requiredKeys<T extends object> = {
[k in keyof T]: {} extends Pick<T, k> ? never : k;

type requiredKeys<T extends ZodRawShape, I extends ObjectField> = {
[k in keyof T]: T[k] extends ZodOptional<ZodTypeAny>
? never
: T[k] extends ZodDefault<ZodTypeAny>
? I extends '_output' ? k : never
: k;
}[keyof T];
export type addQuestionMarks<T extends object, _O = any> = {
[K in requiredKeys<T>]: T[K];

export type addQuestionMarks<Shape extends ZodRawShape, I extends '_output' | '_input', _O = any> = {
[K in requiredKeys<Shape, I>]: Shape[K][I];
} & {
[K in optionalKeys<T>]?: T[K];
} & { [k in keyof T]?: unknown };
[K in optionalKeys<Shape, I>]?: Shape[K][I] | undefined;
} & { [k in keyof Shape]?: unknown };

export type identity<T> = T;
export type flatten<T> = identity<{ [k in keyof T]: T[k] }>;
Expand Down
14 changes: 2 additions & 12 deletions deno/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2330,27 +2330,17 @@ export type objectOutputType<
Shape extends ZodRawShape,
Catchall extends ZodTypeAny,
UnknownKeys extends UnknownKeysParam = UnknownKeysParam
> = objectUtil.flatten<
objectUtil.addQuestionMarks<baseObjectOutputType<Shape>>
> &
> = objectUtil.flatten<objectUtil.addQuestionMarks<Shape, "_output">> &
CatchallOutput<Catchall> &
PassthroughType<UnknownKeys>;

export type baseObjectOutputType<Shape extends ZodRawShape> = {
[k in keyof Shape]: Shape[k]["_output"];
};

export type objectInputType<
Shape extends ZodRawShape,
Catchall extends ZodTypeAny,
UnknownKeys extends UnknownKeysParam = UnknownKeysParam
> = objectUtil.flatten<baseObjectInputType<Shape>> &
> = objectUtil.flatten<objectUtil.addQuestionMarks<Shape, "_input">> &
CatchallInput<Catchall> &
PassthroughType<UnknownKeys>;
export type baseObjectInputType<Shape extends ZodRawShape> =
objectUtil.addQuestionMarks<{
[k in keyof Shape]: Shape[k]["_input"];
}>;

export type CatchallOutput<T extends ZodType> = ZodType extends T
? unknown
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/object.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,9 @@ test("inferred type for unknown/any keys", () => {
myType,
{
anyOptional?: any;
anyRequired?: any;
anyRequired: any;
unknownOptional?: unknown;
unknownRequired?: unknown;
unknownRequired: unknown;
}
>(true);
});
Expand Down
30 changes: 22 additions & 8 deletions src/helpers/util.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ZodDefault, ZodOptional, ZodRawShape, ZodTypeAny } from "../types";

export namespace util {
type AssertEqual<T, U> = (<V>() => V extends T ? 1 : 2) extends <
V
Expand Down Expand Up @@ -101,17 +103,29 @@ export namespace objectUtil {
[k in Exclude<keyof U, keyof V>]: U[k];
} & V;

type optionalKeys<T extends object> = {
[k in keyof T]: {} extends Pick<T, k> ? k : never;
type ObjectField = '_output' | '_input';

type optionalKeys<T extends ZodRawShape, I extends ObjectField> = {
[k in keyof T]: T[k] extends ZodOptional<ZodTypeAny>
? k
: T[k] extends ZodDefault<ZodTypeAny>
? I extends '_output' ? never : k
: never;
}[keyof T];
type requiredKeys<T extends object> = {
[k in keyof T]: {} extends Pick<T, k> ? never : k;

type requiredKeys<T extends ZodRawShape, I extends ObjectField> = {
[k in keyof T]: T[k] extends ZodOptional<ZodTypeAny>
? never
: T[k] extends ZodDefault<ZodTypeAny>
? I extends '_output' ? k : never
: k;
}[keyof T];
export type addQuestionMarks<T extends object, _O = any> = {
[K in requiredKeys<T>]: T[K];

export type addQuestionMarks<Shape extends ZodRawShape, I extends '_output' | '_input', _O = any> = {
[K in requiredKeys<Shape, I>]: Shape[K][I];
} & {
[K in optionalKeys<T>]?: T[K];
} & { [k in keyof T]?: unknown };
[K in optionalKeys<Shape, I>]?: Shape[K][I] | undefined;
} & { [k in keyof Shape]?: unknown };

export type identity<T> = T;
export type flatten<T> = identity<{ [k in keyof T]: T[k] }>;
Expand Down
14 changes: 2 additions & 12 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2330,27 +2330,17 @@ export type objectOutputType<
Shape extends ZodRawShape,
Catchall extends ZodTypeAny,
UnknownKeys extends UnknownKeysParam = UnknownKeysParam
> = objectUtil.flatten<
objectUtil.addQuestionMarks<baseObjectOutputType<Shape>>
> &
> = objectUtil.flatten<objectUtil.addQuestionMarks<Shape, "_output">> &
CatchallOutput<Catchall> &
PassthroughType<UnknownKeys>;

export type baseObjectOutputType<Shape extends ZodRawShape> = {
[k in keyof Shape]: Shape[k]["_output"];
};

export type objectInputType<
Shape extends ZodRawShape,
Catchall extends ZodTypeAny,
UnknownKeys extends UnknownKeysParam = UnknownKeysParam
> = objectUtil.flatten<baseObjectInputType<Shape>> &
> = objectUtil.flatten<objectUtil.addQuestionMarks<Shape, "_input">> &
CatchallInput<Catchall> &
PassthroughType<UnknownKeys>;
export type baseObjectInputType<Shape extends ZodRawShape> =
objectUtil.addQuestionMarks<{
[k in keyof Shape]: Shape[k]["_input"];
}>;

export type CatchallOutput<T extends ZodType> = ZodType extends T
? unknown
Expand Down

0 comments on commit 1d0258d

Please sign in to comment.