10000 How to use Zod schema with existing TypeScript interface? · Issue #2807 · colinhacks/zod · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

How to use Zod schema with existing TypeScript interface? #2807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
JacobWeisenburger opened this issue Sep 25, 2023 Discussed in #2796 · 34 comments
Open

How to use Zod schema with existing TypeScript interface? #2807

JacobWeisenburger opened this issue Sep 25, 2023 Discussed in #2796 · 34 comments
Labels
duplicate This issue or pull request already exists

Comments

@JacobWeisenburger
Copy link
Contributor

Discussed in #2796

Originally posted by adamerose September 24, 2023
I see Zod described as a "TypeScript-first" library but am struggling to figure out how to actually integrate it with my TypeScript definitions. Is there a way to pass a TypeScript interface to my Zod schema as a generic and have my linter tell me if they're not compatible?

This is how it looks in Yup:

import { object, number, string, ObjectSchema } from 'yup';

interface Person {
  name: string;
  age?: number;
  sex: 'male' | 'female' | 'other' | null;
}

// will raise a compile-time type error if the schema does not produce a valid Person
const schema: ObjectSchema<Person> = object({
  name: string().defined(),
  age: number().optional(),
  sex: string<'male' | 'female' | 'other'>().nullable().defined(),
});

// ❌ errors:
// "Type 'number | undefined' is not assignable to type 'string'."
const badSchema: ObjectSchema<Person> = object({
  name: number(),
});

I found discussion here and here but didn't see any solution I could understand, or relied on codegen and external libraries.

I think something like this might work? But it's very boilerplatey and I don't see anything like this in the docs:

interface Person {
  name: string;
  age?: number;
  sex: "male" | "female" | "other" | null;
}

const zodSchema = z.object({
  name: z.string(),
  age: z.number().optional(),
  sex: z.enum(["male", "female", "other"]).nullable(),
});

type InferredPerson = z.infer<typeof zodSchema>;
function assertType<T>(_value: T) {}
assertType<Person>({} as InferredPerson);
```</div>
@JacobWeisenburger JacobWeisenburger added the duplicate This issue or pull request already exists label Sep 25, 2023
@graup
Copy link
graup commented Sep 26, 2023

In practice (assuming that you actually do something with your parsed data), wouldn't code like this be enough to do such a check?

import z from 'zod';

interface Person {
  name: string;
  age?: number;
  sex: "male" | "female" | "other" | null;
}

function doSomethingWithPerson(person: Person) {
 return person;
}

const badSchema = z.object({
  name: z.number(),
});

const personBad = badSchema.parse({ name: 'foo' });

doSomethingWithPerson(personBad);
// ❌ Argument of type '{ name: number; }' is not assignable to parameter of type 'Person'.
//    Property 'sex' is missing in type '{ name: number; }' but required in type 'Person'.(2345)

This will throw an error if your schema does not match the interface.

I'm not sure why you would need an additional assertion, but if you do, code like you suggested would be fine for that.

@mikeyaa
Copy link
mikeyaa commented Oct 8, 2023

I'm also wondering how this can be achieved. I have an existing interface I'd like to make sure that my zod schema always matches. It's easy enough to run my existing interface thru a zod schema generator and generate the matching zod schema but that does not ensure it stays in sync over time. If the original interface is updated, I want a compile time error telling me to update my zod schema. This is is possible with the io-ts library but I am struggling to figure out how to do it with zod.

@adriangabardo
Copy link

Adding my two cents to this. I also don't see any solution actually being given in issues such as #53.

I don't want Zod to determine what my TypeScript types and interfaces look like, I want my TypeScript types and interfaces to determine what Zod looks like.

I would like:

interface Car {
make: string;
model: string;
}

To lead to:

z.object({ make: z.string(), model: z.string() });

Not the other way around.

As awesome as Zod is, and how eager I was to use it, I put more trust into TypeScript being around and maintained for a long time than I do in Zod. If for any reason I need to move away from Zod in the future, if all my types are inferred through Zod, that becomes a real issue.

As eager as I was to use Zod on my current project, I will probably choose something else instead because of this design choice.

@ctsstc
Copy link
ctsstc commented Dec 12, 2023

I think because TS is a superset of JS and an interface doesn't actually exist in JS-land, there's nothing tangible from an interface come transpile/run time. Ultimately there would need to be something that generated code at build, or even a code generator before build/run time that would take TS interfaces and build out the associated code validation (ie: Zod). Maybe there's some type of reflection that could be utilized to help do this?

Some interesting links around such:

@ariadng
Copy link
ariadng commented Jan 6, 2024

Hello, everyone.

Actually we can achieve the inverse. Instead of using TypeScript interface for Zod's schema, we can use the Zod's "infer" functionality to get TypeScript type definition from Zod's schema. Here is the example:

const PersonSchema = z.object({
	name: z.string(),
});

export type Person = z.infer<typeof PersonSchema>;

The TypeScript type checking also works this way:

Screenshot 2024-01-06 222342

@m10rten
Copy link
Contributor
m10rten commented Feb 12, 2024

I think the prefered way of using it / the perfect solution would be something like:

//simple interface or type with any properties, most likely generated
interface Human {
  name: string;
  age?: number;
}

// to any zod-schema:
const schema = z.from<Human>();
// or even something like this would be OK:
class Person implements Human {
// ... constructor e
8000
tc
}
const another = z.create(Person);

// you would parse as normal
const unknownData = {propA: "a", name: "john", age: "123"}

const parsed = schema.parse(unknownData);
// ^? = typeof Human

// same for .safeParse and safeParseAsync

Would that even be possible to extract properties and types from an interface?

@graup
Copy link
graup commented Feb 12, 2024

Would that even be possible to extract properties and types from an interface?

This is kind of backwards and not at all the goal of zod. It is not possible to create runtime-code from TS without additional build steps. Zod is a JS library, not a build tool. But someone else could create such a "ts-to-zod" bundler plugin, which could use typescript AST parsing to generate new JS code at build time. However, then your TS types become the source of truth, and those are less expressive than what zod provides.

/edit: just saw that someone built this: https://github.com/fabien0102/ts-to-zod

@m10rten
Copy link
Contributor
m10rten commented Feb 15, 2024

However, then your TS types become the source of truth, and those are less expressive than what zod provides.

Let's say you have a database and an ORM to talk to it, that ORM has types but no validation.
So what do you want: A zod validation over an existing type/class/object.

I think this could be done during runtime, how I am not sure.

@sugarp
Copy link
sugarp commented Mar 1, 2024

Adding my two cents to this. I also don't see any solution actually being given in issues such as #53.

I don't want Zod to determine what my TypeScript types and interfaces look like, I want my TypeScript types and interfaces to determine what Zod looks like.

I would like:

interface Car {
make: string;
model: string;
}

To lead to:

z.object({ make: z.string(), model: z.string() });

Not the other way around.

As awesome as Zod is, and how eager I was to use it, I put more trust into TypeScript being around and maintained for a long time than I do in Zod. If for any reason I need to move away from Zod in the future, if all my types are inferred through Zod, that becomes a real issue.

As eager as I was to use Zod on my current project, I will probably choose something else instead because of this design choice.

I'd like to add to this that it's not just about trust but also about source of truth being generated by other tools. Today many types get generated from for instance Prisma or GQL schemas. With zod I have no clean mean of ensuring that schema structure is up to date with types that it's supposed to validate.
Not only that I can't have the schema implement certain interface but also argument of parse is of type unknown so there is no type safety at all.

@schicks
Copy link
Contributor
schicks commented Mar 4, 2024

This is still boilerplatey, but what about something like this?

interface Car {
make: string;
model: string;
}

const Car = z.object({
    make: z.string(),
    model: z.string()
}) satisfies ZodType<Car>

playground link

This guarantees that the zod schema you define is in sync with the interface that already exists. I've used this approach a few times when the type I'm modeling comes from some other space I don't control (codegen, upstream libraries, etc.).

@m10rten
Copy link
Contributor
m10rten commented Mar 5, 2024

This is still boilerplatey, but what about something like this?

interface Car {
make: string;
model: string;
}

const Car = z.object({
    make: z.string(),
    model: z.string()
}) satisfies ZodType<Car>

playground link

This guarantees that the zod schema you define is in sync with the interface that already exists. I've used this approach a few times when the type I'm modeling comes from some other space I don't control (codegen, upstream libraries, etc.).

It does, but at the same time you have to redefine your properties that are already set in the interface, but for zod.

@schicks
Copy link
Contributor
schicks commented Mar 5, 2024

Yeah, as @graup mentioned, I don't think that's a thing zod is going to handle itself; to generate zod parsers from static types you'll need codegen tools like ts-to-zod. My experience is that codegen tools tend to be painful to work with and lead to bad dev experiences, so I tend to opt for the statically checked repetition instead.

That may be because in my cases it's usually not properties that I've already set; if I'm doing this, it's because I'm trying to create a zod parser for a type I don't control. If I did control it, I'd just derive the type from the zod parser.

@Papooch
Copy link
Papooch commented Apr 12, 2024

I've used the Joi schema validator before, which has this exact feature - treating your TS types as the source of truth:

import * as Joi from 'joi'

interface Car {
  make: string;
  model: string;
}

const Car = Joi.object<Car>({
    make: Joi.string(),
    model: Joi.string()
})

Truth be told, it doesn't support the inverse (what Zod does).

So, if you need your schema validation library to support defining a schema based on your TS type, you can look for alternatives like Joi.

However, it would be awesome if Zod supported this workflow as well.

@paleo
Copy link
paleo commented May 6, 2024

I've used the Joi schema validator before, which has this exact feature - treating your TS types as the source of truth

@Papooch It ensures only that you don't declare additional keys. It doesn't ensure everything else. In your example, Joi is happy with a wrong schema:

const Car = Joi.object<Car>({
  make: Joi.boolean(), // wrong type
  // missing key `model`
});

@falexandrou
Copy link

@paleo My experience is different with version 17.13.1: If I enable the strict type check (which I'm not sure it's false by default), I get a pretty decent type-check.

For example, the following behaves as you describe:

interface Car {
  make: string
  model: string
}

const Car = Joi.object<Car>({
  make: Joi.boolean(),
  model: Joi.string(),
})

However if I switch to:

const Car = Joi.object<Car, true>({ // <--- notice the "true" here
  make: Joi.boolean(),
  model: Joi.string(),
})

gives me a

Type 'BooleanSchema<boolean>' is missing the following properties from type 'StringSchema<string>': alphanum, base64, case, creditCard, and 24 more.ts(2740)

@Athrun-Judah
Copy link

I found this: https://github.com/jbranchaud/til/blob/master/zod/incorporate-existing-type-into-zod-schema.md
The author uses zodType, such as:

const customSchema: z.zodType<CustomType> = z.any();
const schema = z.object({
    custom: customSchema.array();
})

@schicks
Copy link
8000 Contributor
schicks commented May 31, 2024

@Athrun-Judah this will only validate that the key custom maps to an array; it doesn't validate that the elements of the array match CustomType, it just assumes that they do. That might be fine, but is no safer than casting the elements of the array like this;

type CustomType = {a: 'particular shape'}
const schema = z.object({custom: z.any().array()})
const unvalidated = {custom: ['anything', 3, {at: 'all'}, null,undefined]}
const validated: {custom: CustomType[]} = schema.parse(unvalidated)

@ylc395
Copy link
ylc395 commented Jun 24, 2024

I wrote a tool https://github.com/ylc395/zodify

You can use this tool to generate schemas from TypeScript types.

@jinyongp
Copy link
jinyongp commented Jun 27, 2024

satisfies z.ZodType<User> seems to be working well, but it doesn't tell you the missing key exactly.

I'm using a mapped type by defining ToZodSchema. It works well with nullable and optional.

type IsNullable<T> = Extract<T, null> extends never ? false : true
type IsOptional<T> = Extract<T, undefined> extends never ? false : true

{
  type T1 = IsNullable<string> // false
  type T2 = IsNullable<string | null> // true
  type T3 = IsNullable<string | undefined> // false
  type T4 = IsNullable<string | null | undefined> // true

  type T5 = IsOptional<string> // false
  type T6 = IsOptional<string | null> // false
  type T7 = IsOptional<string | undefined> // true
  type T8 = IsOptional<string | null | undefined> // true
}

type ZodWithEffects<T extends z.ZodTypeAny> = T | z.ZodEffects<T, unknown, unknown>

export type ToZodSchema<T extends Record<string, any>> = {
  [K in keyof T]-?: IsNullable<T[K]> extends true
    ? ZodWithEffects<z.ZodNullable<z.ZodType<T[K]>>>
    : IsOptional<T[K]> extends true
      ? ZodWithEffects<z.ZodOptional<z.ZodType<T[K]>>>
      : ZodWithEffects<z.ZodType<T[K]>>
}

interface Foo {
  bar: string
  withEffects: string
  nullable: number | null
  optional?: string
  optionalNullable?: string | null
  omitted?: string
}

const schema = z.object({
  bar: z.string(),
  withEffects: z.preprocess(val => val, z.string()),
  // bar: z.number() // Type 'ZodNumber' is not assignable to type 'ZodType<string, ZodTypeDef, string>'.
  // baz: z.number(), // Object literal may only specify known properties, and 'baz' does not exist in type 'Schema<Foo>'.ts(2353)
  nullable: z.number().nullable(),
  optional: z.string().optional(),
  optionalNullable: z.string().optional().nullable(), // should chain optional first
  // omitted: z.string().optional(),
} satisfies ToZodSchema<Foo>) // error: Property 'omitted' is missing in type

@LeulAria
Copy link
LeulAria commented Aug 8, 2024

any update on this ? how can i use existing interface to construct my zod schema

@graup
Copy link
graup commented Aug 8, 2024

@LeulAria There have been multiple suggested solutions in this thread. The TL;DR is that this isn't something that zod does but it can be done with other tools.

@krishnagkmit
Copy link
krishnagkmit commented Aug 21, 2024

I think this might work - z.custom

interface IRate {
  id: string;
  name: string;
}

const LocationFormSchema = z.object({
  name: z.string().min(1, { message: 'Name is required and cannot be empty' }),
  rate: z.custom<IRate>().optional(),
});

in this my rate object is defined based on the interface IRate and when assigning a rate in a zod object does not gives any type error

@minecrawler
Copy link

I think this might work - z.custom

Taking a look at the docs, while this will give proper typing, it will diminish zod's usefulness as a validating tool. Without supplying a custom validator, you can leave out zod completely and have the same value at better readability.

@EverNife
Copy link

satisfies z.ZodType<User> seems to be working well, but it doesn't tell you the missing key exactly.

I'm using a mapped type by defining ToZodSchema. It works well with nullable and optional.

type IsNullable<T> = Extract<T, null> extends never ? false : true
type IsOptional<T> = Extract<T, undefined> extends never ? false : true

{
  type T1 = IsNullable<string> // false
  type T2 = IsNullable<string | null> // true
  type T3 = IsNullable<string | undefined> // false
  type T4 = IsNullable<string | null | undefined> // true

  type T5 = IsOptional<string> // false
  type T6 = IsOptional<string | null> // false
  type T7 = IsOptional<string | undefined> // true
  type T8 = IsOptional<string | null | undefined> // true
}

type ZodWithEffects<T extends z.ZodTypeAny> = T | z.ZodEffects<T, unknown, unknown>

export type ToZodSchema<T extends Record<string, any>> = {
  [K in keyof T]-?: IsNullable<T[K]> extends true
    ? ZodWithEffects<z.ZodNullable<z.ZodType<T[K]>>>
    : IsOptional<T[K]> extends true
      ? ZodWithEffects<z.ZodOptional<z.ZodType<T[K]>>>
      : ZodWithEffects<z.ZodType<T[K]>>
}

interface Foo {
  bar: string
  withEffects: string
  nullable: number | null
  optional?: string
  optionalNullable?: string | null
  omitted?: string
}

const schema = z.object({
  bar: z.string(),
  withEffects: z.preprocess(val => val, z.string()),
  // bar: z.number() // Type 'ZodNumber' is not assignable to type 'ZodType<string, ZodTypeDef, string>'.
  // baz: z.number(), // Object literal may only specify known properties, and 'baz' does not exist in type 'Schema<Foo>'.ts(2353)
  nullable: z.number().nullable(),
  optional: z.string().optional(),
  optionalNullable: z.string().optional().nullable(), // should chain optional first
  // omitted: z.string().optional(),
} satisfies ToZodSchema<Foo>) // error: Property 'omitted' is missing in type

Thats good, nice @jinyongp !

I took your code and just created a zod.ts file under utils folder and thats it.

import { ZodEffects, ZodNullable, ZodOptional, ZodType, ZodTypeAny } from 'zod';

type IsNullable<T> = Extract<T, null> extends never ? false : true;
type IsOptional<T> = Extract<T, undefined> extends never ? false : true;

type ZodWithEffects<T extends ZodTypeAny> = T | ZodEffects<T, unknown, unknown>;

export type ToZodSchema<T extends Record<string, any>> = {
  [K in keyof T]-?: IsNullable<T[K]> extends true
    ? ZodWithEffects<ZodNullable<ZodType<T[K]>>>
    : IsOptional<T[K]> extends true
      ? ZodWithEffects<ZodOptional<ZodType<T[K]>>>
      : ZodWithEffects<ZodType<T[K]>>;
};

@mkatrenik
Copy link

@schicks solution worked for me until i had .default() on one of the fields (it caused difference in inferred Input and Output generic params of ZodType) , so now i use this

export const BaseClient = z.object({
  id: z.number().positive(),
  name: z.string().min(2).max(200),
  ...
});
BaseClient._output satisfies Tables.SelectClient;

@schicks
Copy link
Contributor
schicks commented Oct 27, 2024

@mkatrenik that just taught me so much about how zod holds onto type information on parsers. Very cool!

@HaileyesusZ
Copy link

@JacobWeisenburger
I was in a slightly nuanced situation where I have a component that only expects a variation of zod schema from a specific interface.
If you do schema : ZodType<Partial<Person>> on the component, then you can't use some utility methods such as schema.merge that are available in ZodObject (ZodType is too generic).

So the solution I ended up using is:

type PersonSchema = ZodObject<{
  [K in keyof Partial<Person>]: K extends keyof Person ? ZodType<Person[K]> : never;
}>;

Then in your component,

schema: PersonSchema

You can also do satisfies PersonSchema after defining the actual schema (as per @schicks suggestions).

Full example,

interface Car {
 make: string;
 model?: string;
}

type CarSchema = ZodObject<{
  [K in keyof Partial<Car>]: K extends keyof Car ? ZodType<Car[K]> : never;
}>;

function MyForm(props :  { initialValues?: Partial<Car>, schema: CarSchema }) {
      // const newSchema = schema.merge(anotherSchema);
    .....

}

const Car = z.object({
    make: z.string(),
    model: z.string()
}) satisfies CarSchema;

// works because I used Partial<Car> in CarSchema
const Car2= z.object({
    make: z.string(),
}) satisfies CarSchema;

MyForm({ schema: Car })
MyForm({ schema: Car2 })

@LeulAria in case this is useful to you

@Choco-milk-for-u
Copy link

is there already any good solutions yet?

@c5n8
Copy link
c5n8 commented Feb 16, 2025

@Choco-milk-for-u no

@Choco-milk-for-u
Copy link

@Choco-milk-for-u no

thank you.
this is pretty wild ngl.

@markbulingit
Copy link

@schicks solution worked for me until i had .default() on one of the fields (it caused difference in inferred Input and Output generic params of ZodType) , so now i use this

export const BaseClient = z.object({
  id: z.number().positive(),
  name: z.string().min(2).max(200),
  ...
});
BaseClient._output satisfies Tables.SelectClient;

@mkatrenik I asked ChatGPT Premium to resolve this, here's the fully working code

import {
  ZodDefault,
  ZodEffects,
  ZodNullable,
  ZodOptional,
  ZodType,
  ZodTypeAny,
} from "zod";

// Helper type to determine if a type is nullable
type IsNullable<T> = T extends null | undefined ? true : false;
// Helper type to determine if a type is optional
type IsOptional<T> = T extends undefined ? true : false;
// Helper type to check if the type is a default type
type IsDefault<T> = T extends ZodDefault<any> ? true : false;

// Type to apply Zod effects to a schema
type ZodWithEffects<T extends ZodTypeAny> =
  | T
  | ZodEffects<T, unknown, unknown>
  | ZodDefault<T>;

// Main type that maps a record type to Zod schema types
export type ToZodSchema<T extends Record<string, any>> = {
  [K in keyof T]: IsNullable<T[K]> extends true
    ? ZodWithEffects<ZodNullable<ZodType<T[K]>>>
    : IsOptional<T[K]> extends true
    ? ZodWithEffects<ZodOptional<ZodType<T[K]>&
10000
gt;>
    : IsDefault<T[K]> extends true
    ? ZodWithEffects<ZodDefault<ZodType<T[K]>>>
    : ZodWithEffects<ZodType<T[K]>>;
};

@thevuong
Copy link

Thanks to @HaileyesusZ , whose solution I've enhanced to create this custom type.

What is SchemaFromInterface?

type SchemaFromInterface<T> = ZodObject<{
 [K in keyof Partial<T>]: K extends keyof T ? ZodType<T[K]> : never;
}>;

This type alias allows you to create Zod schemas from existing TypeScript interfaces,
ensuring your Zod schemas exactly match your interfaces.

How to Use It

  1. Define your TypeScript interface
interface User {
 id: string;
 name: string;
 age: number;
 email?: string;
}
  1. Create a Zod schema with the satisfies operator
const UserSchema = z.object({
 id: z.string(),
 name: z.string(),
 age: z.number(),
 email: z.string().email().optional(),
}) satisfies SchemaFromInterface<User>;

Benefits

  • Type-safety: Ensures Zod schemas match TypeScript interfaces
  • Reduces duplication: No need to define separate types for schemas
  • Maintainability: TypeScript will error if schemas don't match interfaces
  • Partial schema support: Easily create partial schemas with .partial()

Advanced Example

// Define interfaces
interface Product {
 id: string;
 price: number;
 inStock: boolean;
 tags?: string[];
}

// Create schema
const ProductSchema = z.object({
 id: z.string(),
 price: z.number().positive(),
 inStock: z.boolean(),
 tags: z.array(z.string()).optional(),
}) satisfies SchemaFromInterface<Product>;

// Create partial schema (for updates/patches)
const ProductUpdateSchema = ProductSchema.partial();

// Create type from schema
type ProductFromSchema = z.infer<typeof ProductSchema>;

SchemaFromInterface combines TypeScript's type safety with Zod's runtime validation,
making your applications safer and more maintainable.

@Quang-Dong
Copy link

satisfies z.ZodType<User> seems to be working well, but it doesn't tell you the missing key exactly.

I'm using a mapped type by defining ToZodSchema. It works well with nullable and optional.

type IsNullable = Extract<T, null> extends never ? false : true
type IsOptional = Extract<T, undefined> extends never ? false : true

{
type T1 = IsNullable // false
type T2 = IsNullable<string | null> // true
type T3 = IsNullable<string | undefined> // false
type T4 = IsNullable<string | null | undefined> // true

type T5 = IsOptional // false
type T6 = IsOptional<string | null> // false
type T7 = IsOptional<string | undefined> // true
type T8 = IsOptional<string | null | undefined> // true
}

type ZodWithEffects = T | z.ZodEffects<T, unknown, unknown>

export type ToZodSchema<T extends Record<string, any>> = {
[K in keyof T]-?: IsNullable<T[K]> extends true
? ZodWithEffects<z.ZodNullable<z.ZodType<T[K]>>>
: IsOptional<T[K]> extends true
? ZodWithEffects<z.ZodOptional<z.ZodType<T[K]>>>
: ZodWithEffects<z.ZodType<T[K]>>
}

interface Foo {
bar: string
withEffects: string
nullable: number | null
optional?: string
optionalNullable?: string | null
omitted?: string
}

const schema = z.object({
bar: z.string(),
withEffects: z.preprocess(val => val, z.string()),
// bar: z.number() // Type 'ZodNumber' is not assignable to type 'ZodType<string, ZodTypeDef, string>'.
// baz: z.number(), // Object literal may only specify known properties, and 'baz' does not exist in type 'Schema'.ts(2353)
nullable: z.number().nullable(),
optional: z.string().optional(),
optionalNullable: z.string().optional().nullable(), // should chain optional first
// omitted: z.string().optional(),
} satisfies ToZodSchema) // error: Property 'omitted' is missing in type

Best solution.

@ShahriarKh
Copy link

An here's an even more improved example that works with .refine():

type Zodd<T> = ZodObject<{
  [K in keyof Partial<T>]: K extends keyof T ? ZodType<T[K]> : never;
}>;

export type ZodSatisfies<T> = Zodd<T> | ZodEffects<Zodd<T>>;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

0