英文:
How to override interface in a function and maintain the correct return type?
问题
我有一个definePlugin
函数,它接受一个实现PluginConfig
接口的对象,其中包含一个默认的RequestValidator
。我想要覆盖这个接口,提供一个自定义的验证器,同时确保返回的值具有正确的类型。然而,我的当前实现导致返回的vueAuth
具有any
类型。如何在覆盖接口的同时保持正确的返回类型?以下是我的当前代码:
import { z } from 'zod'
// 创建默认验证器
export const EmailValidator = z.object({
email: z
.string({ required_error: 'auth.validation.email' })
.email({ message: 'auth.validation.email_format' })
})
// 泛型应至少实现的默认接口
interface PluginConfig {
validator?: z.ZodType
}
// 带有默认验证器的默认接口,如果没有覆盖,则使用它
interface DefaultPluginConfig {
validator?: typeof EmailValidator
}
const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
validator = EmailValidator
}: T) => {
return validator.parse({})
}
const test = definePlugin({})
// 对象应该看起来像EmailValidator,但实际上是any
test.email
// 创建自定义验证器
const CustomValidator = z.object({
email: z.string(),
username: z.string()
})
// 创建自定义通用接口
interface CustomConfig {
validator?: typeof CustomValidator
}
const test2 = definePlugin<CustomConfig>({
validator: CustomValidator
})
// 对象应该看起来像CustomValidator,但实际上是any
test2.username
test2.username
我应该进行哪些更改以确保在提供自定义验证器时,definePlugin
具有正确的类型?
英文:
I have a definePlugin function that accepts an object implementing the PluginConfig interface, with a default RequestValidator. I’m trying to override this interface, providing a custom validator while ensuring the returned value has the correct type. However, my current implementation results in the returned vueAuth having the type any. How do I override the interface while maintaining the correct return type?
Here’s my current code:
import { z } from 'zod'
// Create de default validator
export const EmailValidator = z.object({
email: z
.string({ required_error: 'auth.validation.email' })
.email({ message: 'auth.validation.email_format' })
})
// The default interface the generic should at least implement
interface PluginConfig {
validator?: z.ZodType
}
// The default interface with the default validator to use if not overriden
interface DefaultPluginConfig {
validator?: typeof EmailValidator
}
const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
validator = EmailValidator
}: T) => {
return validator.parse({})
}
const test = definePlugin({})
// Object should look like EmailValidator but is any instead
test.email
// Create a custom validator
const CustomValidator = z.object({
email: z.string(),
username: z.string()
})
// Create a custom interface to use as generic
interface CustomConfig {
validator?: typeof CustomValidator
}
const test2 = definePlugin<CustomConfig>({
validator: CustomValidator
})
// Object should look like CustomValidator but is any instead
test2.username
test2.username
What changes should I make to ensure that definePlugin has the correct type when providing a custom validator?
答案1
得分: 2
以下是翻译好的部分:
你的代码中存在一些问题,导致了一些问题:
-
你已经将
PluginConfig
定义为属性z.ZodType
,但它是一种类型而不是一个值。你可以修改如下:interface PluginConfig {
validator?: z.Schema<any>;
} -
在你的代码中,你的
DefaultPluginConfig
接口没有正确地扩展PluginConfig
。你可以将其更改为:interface DefaultPluginConfig extends PluginConfig {
validator?: typeof EmailValidator;
}
你的代码应该如下所示:
import { z } from 'zod';
// 创建默认验证器
export const EmailValidator = z.object({
email: z
.string({ required_error: 'auth.validation.email' })
.email({ message: 'auth.validation.email_format' }),
});
// 泛型应该至少实现的默认接口
interface PluginConfig {
validator?: z.Schema<any>;
}
// 带有默认验证器的默认接口,如果未被覆盖,则使用此验证器
interface DefaultPluginConfig extends PluginConfig {
validator?: typeof EmailValidator;
}
const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
validator = EmailValidator,
}: T) => {
return validator.parse({});
};
const test = definePlugin({});
test.email; // 对象具有正确的类型EmailValidator
// 创建自定义验证器
const CustomValidator = z.object({
email: z.string(),
username: z.string(),
});
// 创建用作泛型的自定义接口
interface CustomConfig extends PluginConfig {
validator?: typeof CustomValidator;
}
const test2 = definePlugin<CustomConfig>({
validator: CustomValidator,
});
test2.username; // 对象具有正确的类型CustomValidator
test2.email; // 对象具有正确的类型CustomValidator(来自CustomValidator)
如果你仍然遇到任何问题,请告诉我。
更新后的代码(最终版):
根据聊天讨论,正确的答案如下:
import { z, ZodType } from "zod";
// 创建默认验证器
export const EmailValidator = z.object({
email: z.string().default("")
});
// 泛型至少应该实现的默认接口
interface PluginConfig<T extends ZodType = typeof EmailValidator> {
validator?: T;
}
const definePlugin = <
T extends PluginConfig = PluginConfig<typeof EmailValidator>,
R = T extends PluginConfig<infer V> ? V : ZodType
>({
validator = EmailValidator
}: T): R extends ZodType<infer P> ? P : never => {
return validator.parse({}) as any;
};
const test = definePlugin({});
test.email; // 对象具有正确的类型EmailValidator
const CustomValidator = z.object({
email: z.string().default(""),
username: z.string().default("")
});
type CustomConfig = PluginConfig<typeof CustomValidator>;
const test2 = definePlugin<CustomConfig>({
validator: CustomValidator
});
console.log(test2.username);
在这里,我们使用了泛型 V 和 P 来捕获来自 PluginConfig
和 ZodType
的类型。
在这里,
V
- 代表了 PluginConfig 中验证器属性的推断类型
P
- 代表了 ZodType 的推断类型
这些泛型 V
和 P
允许函数处理不同的验证器配置,并根据它返回适当的推断类型。
英文:
There are some issues in your code which cause problem:
-
You have defined
PluginConfig
to propertyz.ZodType
which is a type not a value. You can modify as:interface PluginConfig {
validator?: z.Schema<any>;
} -
In your code your
DefaultPluginConfig
interface is not properly extendingPluginConfig
. You can change this as:interface DefaultPluginConfig extends PluginConfig {
validator?: typeof EmailValidator;
}
Your code would look like:
import { z } from 'zod';
// Create the default validator
export const EmailValidator = z.object({
email: z
.string({ required_error: 'auth.validation.email' })
.email({ message: 'auth.validation.email_format' }),
});
// The default interface the generic should at least implement
interface PluginConfig {
validator?: z.Schema<any>;
}
// The default interface with the default validator to use if not overridden
interface DefaultPluginConfig extends PluginConfig {
validator?: typeof EmailValidator;
}
const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
validator = EmailValidator,
}: T) => {
return validator.parse({});
};
const test = definePlugin({});
test.email; // Object has the correct type EmailValidator
// Create a custom validator
const CustomValidator = z.object({
email: z.string(),
username: z.string(),
});
// Create a custom interface to use as generic
interface CustomConfig extends PluginConfig {
validator?: typeof CustomValidator;
}
const test2 = definePlugin<CustomConfig>({
validator: CustomValidator,
});
test2.username; // Object has the correct type CustomValidator
test2.email; // Object has the correct type CustomValidator (from CustomValidator)
Let me know if you still face any issue.
UPDATED CODE (FINAL):
As per discussion on chat the correct answer is:
import { z, ZodType } from "zod";
// Create the default validator
export const EmailValidator = z.object({
email: z.string().default("")
});
// The default interface the generic should at least implement
interface PluginConfig<T extends ZodType = typeof EmailValidator> {
validator?: T;
}
const definePlugin = <
T extends PluginConfig = PluginConfig<typeof EmailValidator>,
R = T extends PluginConfig<infer V> ? V : ZodType
>({
validator = EmailValidator
}: T): R extends ZodType<infer P> ? P : never => {
return validator.parse({}) as any;
};
const test = definePlugin({});
test.email; // Object has the correct type EmailValidator
const CustomValidator = z.object({
email: z.string().default(""),
username: z.string().default("")
});
type CustomConfig = PluginConfig<typeof CustomValidator>;
const test2 = definePlugin<CustomConfig>({
validator: CustomValidator
});
console.log(test2.username);
So here Generic V and P are used to capture the types from PluginConfig
and ZodType
respectively.
Here,<br>
V
- represents inferred types of validator property in PluginConfig<br>
P
- represents the inferred type of the ZodType
These generics V
and P
allow the function to handle different validator configurations and return suitable inferred type based on it.
答案2
得分: 0
- 基本解决方案是
z.infer
const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
validator = EmailValidator
}: T): z.infer<NonNullable<T['validator']>> => {
return validator.parse({})
}
- 重载是处理调用边缘情况的最佳方法,因为它们具有推理尝试的顺序。我建议使用它们。
function definePlugin({validator}: DefaultPluginConfig): z.infer<NonNullable<DefaultPluginConfig['validator']>>;
function definePlugin<T extends PluginConfig>({validator}: T): z.infer<NonNullable<T['validator']>>;
function definePlugin({validator = EmailValidator}: PluginConfig) {
return validator.parse({})
}
英文:
- The basic solution is to
z.infer
const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
validator = EmailValidator
}: T): z.infer<NonNullable<T['validator']>> => {
return validator.parse({})
}
This will work fine with provided generic parameter, but will fail to provide a default
- Overloads are the best way to work with call corner cases, as they have order of inference tries. I would recommend to use them.
function definePlugin({validator}: DefaultPluginConfig): z.infer<NonNullable<DefaultPluginConfig['validator']>>;
function definePlugin<T extends PluginConfig>({validator}: T): z.infer<NonNullable<T['validator']>>;
function definePlugin({validator = EmailValidator}: PluginConfig) {
return validator.parse({})
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论