Branded object types loose `z.ZodObject` functionalities
When branding an object all the z.ZodObject functionalities are lost, is that the expected behavior or is there something that can be fixed in the types when using z.brand()?
This happens on the latest version 3.19.0.
Here's a minimal reproduction:
export const UserSchema = z.object({
id: z.string(),
name: z.string(),
age: z.number().int().positive().min(18).max(99),
email: z.string().email(),
}).brand<'User'>();
const age = UserSchema.shape.age.parse(28) // Cannot read properties of undefined (reading 'age')
export const UserDomain = UserSchema.strict() // UserSchema.strict() is not a function
.merge( // UserSchema.merge() is not a function
z.strictObject({
id: UserId,
})
);
A workaround is using .unwrap() when trying to call ZodObject functions. Not sure if this is expected behavior and if it is it needs to be documented. Without looking too deeply at the source it's probably a typescript limitation.
Now that I think about it more, it does make sense that a branded object would not have ZodObject functions. Those functions are all mutations on the object which inherently breaks the idea of a branded object which is meant to be immutable. That's why .unwrap() exists. Remove the brand and make the object mutable.
Those functions are all mutations on the object which inherently breaks the idea of a branded object which is meant to be immutable.
Not all of them are mutations necessarily, my reproduction contains one case where I'm not mutating the object: UserSchema.shape.age.parse(28). A branded object still has keys, still has a shape, it might be interesting to have access to those methods in order to iterate, or access a specific property to use its parsing rules
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.
@lukemorales it is not just about mutation. When you brand an object you say: not just the parts have to be valid by itself, but the entire thing has to fulfill some criteria. By branding you "sign" that this properties have passed the validation and transport this result in type system.
When you start parsing separate things, you are either violating the set expectation on the brand or the object brand is simply wrong and shouldn't have been applied. At least a signal that something is possibly not entirely sound.
By having the possibility to unwrap, you can explicitly opt-out. Its harder to do it accidentally. Just because you think or indeed do something correct in an instance, doesn't mean the entire API should now allow doing something that is potentially incorrect.
This is why I think @EvanVujcec is correct.
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.