Reputation: 37431
I need to extend Zod with some custom methods. They don't really have a plugin system so other plugins I've seen use the prototype. This works as intended:
z.ZodType.prototype.disabled = function(isDisabled: boolean): ZodType<any, ZodTypeDef, any> {
this._isDisabled = isDisabled
return this
}
z.ZodType.prototype.isDisabled = function(): boolean {
return this._isDisabled
}
The problem is that we use Typescript, and the original ZodType abstract class obviously doesn't define these extension methods.
I was thinking I could get away with typescript's "interface merging" to extend this, like some other stackoverflow posts recommend, but this isn't an interface - it's an abstract class type.
interface ZodType {
disabled: (isDisabled: boolean) => void
isDisabled: () => boolean
_isDisabled: boolean
}
This doesn't do anything. Here's an example using my custom function:
const schema = z.object({
name: z.string().min(3),
disabled: z.string().disabled(true)
})
This errors with "property 'disabled' does not exist on type ZodString" yet ZodString inherits from ZodType
Is there a way I can properly tell typescript about these changes?
Upvotes: 3
Views: 452
Reputation: 329308
You can definitely merge into the instance side of class
declarations. A class declaration like class Foo { /*...*/ }
brings into scope both the class constructor value named Foo
, and the class instance interface named Foo
, and you can merge into that interface. This is true for abstract
classes as well. So if the class name is ZodType
then you need to merge into interface ZodType {}
.
Since that interface is in a module, you're looking for module augmentation. That's where you import
a module and then merge your definition into an interface inside an appropriate declare module
scope. Like this:
import * as z from 'zod';
declare module 'zod' {
interface ZodType {
disabled(isDisabled: boolean): this;
isDisabled(): boolean;
_isDisabled: boolean
}
}
And now the compiler will know about these definitions, allowing the rest of your code to type check with no errors:
z.ZodType.prototype.disabled = function (isDisabled: boolean) {
this._isDisabled = isDisabled
return this
}
z.ZodType.prototype.isDisabled = function (): boolean {
return this._isDisabled
}
const schema = z.object({
name: z.string().min(3),
disabled: z.string().disabled(true)
})
Upvotes: 1