zod icon indicating copy to clipboard operation
zod copied to clipboard

feat: Add the anyObject type

Open lostpebble opened this issue 1 year ago • 1 comments

Fixes https://github.com/colinhacks/zod/issues/1628 .

This allows the use of z.anyObject() for validation situations where a property is required to be an object but we don't care to validate its exact shape / properties. Other types are easy to define as indiscriminate versions of themselves, except for objects. And using z.any() causes the property to be optional (as noticed in https://github.com/colinhacks/zod/issues/1628).

lostpebble avatar Dec 12 '22 17:12 lostpebble

Deploy Preview for guileless-rolypoly-866f8a ready!

Built without sensitive environment variables

Name Link
Latest commit e24b16d09769eeae15dc167f29a17877c7024ce7
Latest deploy log https://app.netlify.com/sites/guileless-rolypoly-866f8a/deploys/63b580a1ccac490008720d7f
Deploy Preview https://deploy-preview-1675--guileless-rolypoly-866f8a.netlify.app
Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

netlify[bot] avatar Dec 12 '22 17:12 netlify[bot]

Hey guys, what is the status of this one? We started using zod in our company and this is a big issue for us.

niba avatar Jan 04 '23 07:01 niba

I'm not sure that we need z.anyObject because we have z.record(z.any()). It seems to work the way that you have described. Is there anything I'm missing?

const anyObjectSchema = z.record( z.any() )
type AnyObject = z.infer<typeof anyObjectSchema>
// type AnyObject = {
//     [ x: string ]: any
// }

console.log( anyObjectSchema.safeParse( { a: 1, b: 2 } ).success ) // true
console.log( anyObjectSchema.safeParse( {} ).success ) // true
console.log( anyObjectSchema.safeParse( [] ).success ) // false
console.log( anyObjectSchema.safeParse( 'foo' ).success ) // false
console.log( anyObjectSchema.safeParse( 42 ).success ) // false
console.log( anyObjectSchema.safeParse( null ).success ) // false
console.log( anyObjectSchema.safeParse( undefined ).success ) // false

JacobWeisenburger avatar Jan 07 '23 01:01 JacobWeisenburger

This PR was made in response to this comment: https://github.com/colinhacks/zod/issues/1628#issuecomment-1338138675

Also, the inferred type from z.record( z.any()) is { [ x: string ]: any }. This is not exactly the TypeScript object type (which I've made z.anyObject() output to), although they would generally be interchangeable.

I also think that since we have z.object() - having a similarly named z.anyObject() makes more sense than having to find and understand the ability of z.record( z.any()) to do the same. I've never made use of the z.record() type, and while it does work for this situation it feels like a bit of a hack rather than a natural type definition for this situation.

lostpebble avatar Jan 07 '23 11:01 lostpebble

There are situations when a type error arises because a type index is expected but not present. I cannot off hand reproduce the situation though. So I'm not sure to what extent @JacobWeisenburger's point stands--about the need or not for a differentiation. I would expect however that a differentiation is needed under advanced cases, type logic, multiple times derived types, etc.

jasonkuhrt avatar Jan 09 '23 17:01 jasonkuhrt

Either of these seem to do what you are asking for. So I'm still not really understanding what z.anyObject() is supposed to do that other schemas don't.

const emptyObjSchema = z.object( {} )
console.log( emptyObjSchema.safeParse( {} ).success ) // true
console.log( emptyObjSchema.safeParse( { foo: 'foo' } ).success ) // true
console.log( emptyObjSchema.safeParse( [] ).success ) // false
console.log( emptyObjSchema.safeParse( () => { } ).success ) // false
console.log( emptyObjSchema.safeParse( 'foo' ).success ) // false
console.log( emptyObjSchema.safeParse( 42 ).success ) // false
console.log( emptyObjSchema.safeParse( null ).success ) // false
console.log( emptyObjSchema.safeParse( undefined ).success ) // false

const recordSchema = z.record( z.any() )
console.log( recordSchema.safeParse( {} ).success ) // true
console.log( recordSchema.safeParse( { foo: 'foo' } ).success ) // true
console.log( recordSchema.safeParse( [] ).success ) // false
console.log( recordSchema.safeParse( () => { } ).success ) // false
console.log( recordSchema.safeParse( 'foo' ).success ) // false
console.log( recordSchema.safeParse( 42 ).success ) // false
console.log( recordSchema.safeParse( null ).success ) // false
console.log( recordSchema.safeParse( undefined ).success ) // false

JacobWeisenburger avatar Mar 06 '23 22:03 JacobWeisenburger

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 08 '23 11:06 stale[bot]

Either of these seem to do what you are asking for. So I'm still not really understanding what z.anyObject() is supposed to do that other schemas don't.

Its about the returned type when inferring the type from zod. We still want the type to be object instead of any or Record<string, any> or whatnot. Its not an issue of validation per se- its about how we use zod and TypeScript together to define our interfaces.

lostpebble avatar Jul 20 '23 18:07 lostpebble