Skip to content
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

RFC: Make object schemas nonstrict by default #106

Closed
colinhacks opened this issue Aug 1, 2020 · 6 comments
Closed

RFC: Make object schemas nonstrict by default #106

colinhacks opened this issue Aug 1, 2020 · 6 comments

Comments

@colinhacks
Copy link
Owner

colinhacks commented Aug 1, 2020

Zod's policy of disallowing any unknown keys by default is confusing and probably not worth it. It also makes the inferred types incorrect:

const A = z.object({
  a: z.string(),
});

const B = z.object({
  b: z.string(),
});

const AB = z.intersection(A, B);


type AB = z.infer<typeof Teacher>;
// { a: string; b: string };

The actual inferred type here should be never because no data will properly pass validation.

If anyone has a reason why object schemas should be strict by default, speak now!

@chrbala
Copy link
Contributor

chrbala commented Aug 1, 2020

If object schemas become nonstrict by default (which generally makes sense to me), how would parsing work? I see some advantages in being able to give an object with extra keys, then parsing removes them.

const Obj = z.object({
  key: z.string(),
});

Obj.parse({key: 'value', key2: 'value2'})  // could return { key: 'value' }

@hesyifei
Copy link

hesyifei commented Aug 1, 2020

As mentioned in the doc,

TypeScript doesn't allow unknown keys when assigning to an object type, so neither does Zod (by default).

I'm not sure if I'm missing anything here, but wouldn't this make the z.object behaves differently from a TypeScript object type? Personally I feel it's pretty natural to assume z.object works the same way as a TypeScript object, so I am curious about why you think disallowing unknown keys by default is confusing.

It also feels more confusing for parse to silently drop extra keys as suggested by chrbala than for parse to throw an error.


Also it seems that in TypeScript,

An intersection type combines multiple types into one. This allows you to add together existing types to get a single type that has all the features you need. For example, Person & Serializable & Loggable is a Person and Serializable and Loggable. That means an object of this type will have all members of all three types.

So the inferred type (z.infer<typeof Teacher>) is probably correct if we use TypeScript's definition of intersection (I think in part is because the name "intersection" is confusing - see Naming of union and intersection types in TypeScript.)

type A = {
    a: string;
}

type B = {
    b: string;
}

type AB = A & B;

// Valid
const value: AB = {
    a: "a string",
    b: "another string",
}

The actual inferred type here should be never because no data will properly pass validation.

It seems that currently

const AB = z.intersection(A, B);
AB.parse({
  a: "a string",
  b: "another string",
});

which should be valid (based on TypeScript's definition of intersection) shows the error

Error: 2 validation issue(s)

  Issue #0: unrecognized_keys at
  Unrecognized key(s) in object: 'b'

  Issue #1: unrecognized_keys at
  Unrecognized key(s) in object: 'a'

I kind of feel like this is what should be fixed (instead of making object schemas nonstrict by default) so that the intersection works as expected (as in TypeScript)?

@chrbala
Copy link
Contributor

chrbala commented Aug 9, 2020

The primary thing I care about regarding this issue is that there's a path to getting a clean object without erroneous keys. If you use zod to validate before a schemaless database, you might find out you have a bunch of unwanted fields if there aren't good protections in place. Perhaps making this explicit like a ZodSchema.parse(val, { clean: true }) which would allow for this.

Even right now, this is a bit cumbersome because unused fields have to be manually removed. A value cleaner would make this nicer.

@grreeenn
Copy link

grreeenn commented Sep 9, 2020

value cleaner will be a gamechanger.

@colinhacks
Copy link
Owner Author

colinhacks commented Sep 17, 2020

Zod 2 is now in beta and it strips unknown keys by default. It alsso introduces methods for passing through unknown keys (.passthrough()) and throwing errors if unknown keys are encountered (.strict()). https://github.com/vriad/zod/tree/v2#unknown-keys

@chrbala @grreeenn @pkerschbaum @agentlewis @DogPawHat @eh-am @ryardley

@grreeenn
Copy link

grreeenn commented Apr 29, 2021

Zod 2 is now in beta and it strips unknown keys by default.

@colinhacks is this behavior still in place in v3?

upd: sorry for a stupid question; yes, it is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants