如何在函数中覆盖接口并保持正确的返回类型?

huangapple go评论103阅读模式
英文:

How to override interface in a function and maintain the correct return type?

问题

我有一个definePlugin函数,它接受一个实现PluginConfig接口的对象,其中包含一个默认的RequestValidator。我想要覆盖这个接口,提供一个自定义的验证器,同时确保返回的值具有正确的类型。然而,我的当前实现导致返回的vueAuth具有any类型。如何在覆盖接口的同时保持正确的返回类型?以下是我的当前代码:

  1. import { z } from 'zod'
  2. // 创建默认验证器
  3. export const EmailValidator = z.object({
  4. email: z
  5. .string({ required_error: 'auth.validation.email' })
  6. .email({ message: 'auth.validation.email_format' })
  7. })
  8. // 泛型应至少实现的默认接口
  9. interface PluginConfig {
  10. validator?: z.ZodType
  11. }
  12. // 带有默认验证器的默认接口,如果没有覆盖,则使用它
  13. interface DefaultPluginConfig {
  14. validator?: typeof EmailValidator
  15. }
  16. const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
  17. validator = EmailValidator
  18. }: T) => {
  19. return validator.parse({})
  20. }
  21. const test = definePlugin({})
  22. // 对象应该看起来像EmailValidator,但实际上是any
  23. test.email
  24. // 创建自定义验证器
  25. const CustomValidator = z.object({
  26. email: z.string(),
  27. username: z.string()
  28. })
  29. // 创建自定义通用接口
  30. interface CustomConfig {
  31. validator?: typeof CustomValidator
  32. }
  33. const test2 = definePlugin<CustomConfig>({
  34. validator: CustomValidator
  35. })
  36. // 对象应该看起来像CustomValidator,但实际上是any
  37. test2.username
  38. 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:

  1. import { z } from &#39;zod&#39;
  2. // Create de default validator
  3. export const EmailValidator = z.object({
  4. email: z
  5. .string({ required_error: &#39;auth.validation.email&#39; })
  6. .email({ message: &#39;auth.validation.email_format&#39; })
  7. })
  8. // The default interface the generic should at least implement
  9. interface PluginConfig {
  10. validator?: z.ZodType
  11. }
  12. // The default interface with the default validator to use if not overriden
  13. interface DefaultPluginConfig {
  14. validator?: typeof EmailValidator
  15. }
  16. const definePlugin = &lt;T extends PluginConfig = DefaultPluginConfig&gt;({
  17. validator = EmailValidator
  18. }: T) =&gt; {
  19. return validator.parse({})
  20. }
  21. const test = definePlugin({})
  22. // Object should look like EmailValidator but is any instead
  23. test.email
  24. // Create a custom validator
  25. const CustomValidator = z.object({
  26. email: z.string(),
  27. username: z.string()
  28. })
  29. // Create a custom interface to use as generic
  30. interface CustomConfig {
  31. validator?: typeof CustomValidator
  32. }
  33. const test2 = definePlugin&lt;CustomConfig&gt;({
  34. validator: CustomValidator
  35. })
  36. // Object should look like CustomValidator but is any instead
  37. test2.username
  38. test2.username

What changes should I make to ensure that definePlugin has the correct type when providing a custom validator?

答案1

得分: 2

以下是翻译好的部分:

你的代码中存在一些问题,导致了一些问题:

  1. 你已经将PluginConfig定义为属性z.ZodType,但它是一种类型而不是一个值。你可以修改如下:

    interface PluginConfig {
    validator?: z.Schema<any>;
    }

  2. 在你的代码中,你的DefaultPluginConfig接口没有正确地扩展PluginConfig。你可以将其更改为:

    interface DefaultPluginConfig extends PluginConfig {
    validator?: typeof EmailValidator;
    }

你的代码应该如下所示:

  1. import { z } from &#39;zod&#39;;
  2. // 创建默认验证器
  3. export const EmailValidator = z.object({
  4. email: z
  5. .string({ required_error: &#39;auth.validation.email&#39; })
  6. .email({ message: &#39;auth.validation.email_format&#39; }),
  7. });
  8. // 泛型应该至少实现的默认接口
  9. interface PluginConfig {
  10. validator?: z.Schema&lt;any&gt;;
  11. }
  12. // 带有默认验证器的默认接口,如果未被覆盖,则使用此验证器
  13. interface DefaultPluginConfig extends PluginConfig {
  14. validator?: typeof EmailValidator;
  15. }
  16. const definePlugin = &lt;T extends PluginConfig = DefaultPluginConfig&gt;({
  17. validator = EmailValidator,
  18. }: T) =&gt; {
  19. return validator.parse({});
  20. };
  21. const test = definePlugin({});
  22. test.email; // 对象具有正确的类型EmailValidator
  23. // 创建自定义验证器
  24. const CustomValidator = z.object({
  25. email: z.string(),
  26. username: z.string(),
  27. });
  28. // 创建用作泛型的自定义接口
  29. interface CustomConfig extends PluginConfig {
  30. validator?: typeof CustomValidator;
  31. }
  32. const test2 = definePlugin&lt;CustomConfig&gt;({
  33. validator: CustomValidator,
  34. });
  35. test2.username; // 对象具有正确的类型CustomValidator
  36. test2.email; // 对象具有正确的类型CustomValidator(来自CustomValidator)

如果你仍然遇到任何问题,请告诉我。

更新后的代码(最终版)

根据聊天讨论,正确的答案如下:

  1. import { z, ZodType } from &quot;zod&quot;;
  2. // 创建默认验证器
  3. export const EmailValidator = z.object({
  4. email: z.string().default(&quot;&quot;)
  5. });
  6. // 泛型至少应该实现的默认接口
  7. interface PluginConfig&lt;T extends ZodType = typeof EmailValidator&gt; {
  8. validator?: T;
  9. }
  10. const definePlugin = &lt;
  11. T extends PluginConfig = PluginConfig&lt;typeof EmailValidator&gt;,
  12. R = T extends PluginConfig&lt;infer V&gt; ? V : ZodType
  13. &gt;({
  14. validator = EmailValidator
  15. }: T): R extends ZodType&lt;infer P&gt; ? P : never =&gt; {
  16. return validator.parse({}) as any;
  17. };
  18. const test = definePlugin({});
  19. test.email; // 对象具有正确的类型EmailValidator
  20. const CustomValidator = z.object({
  21. email: z.string().default(&quot;&quot;),
  22. username: z.string().default(&quot;&quot;)
  23. });
  24. type CustomConfig = PluginConfig&lt;typeof CustomValidator&gt;;
  25. const test2 = definePlugin&lt;CustomConfig&gt;({
  26. validator: CustomValidator
  27. });
  28. console.log(test2.username);

在这里,我们使用了泛型 V 和 P 来捕获来自 PluginConfigZodType 的类型。

在这里,
V - 代表了 PluginConfig 中验证器属性的推断类型
P - 代表了 ZodType 的推断类型

这些泛型 VP 允许函数处理不同的验证器配置,并根据它返回适当的推断类型。

英文:

There are some issues in your code which cause problem:

  1. You have defined PluginConfig to property z.ZodType which is a type not a value. You can modify as:

    interface PluginConfig {
    validator?: z.Schema<any>;
    }

  2. In your code your DefaultPluginConfig interface is not properly extending PluginConfig. You can change this as:

    interface DefaultPluginConfig extends PluginConfig {
    validator?: typeof EmailValidator;
    }

Your code would look like:

  1. import { z } from &#39;zod&#39;;
  2. // Create the default validator
  3. export const EmailValidator = z.object({
  4. email: z
  5. .string({ required_error: &#39;auth.validation.email&#39; })
  6. .email({ message: &#39;auth.validation.email_format&#39; }),
  7. });
  8. // The default interface the generic should at least implement
  9. interface PluginConfig {
  10. validator?: z.Schema&lt;any&gt;;
  11. }
  12. // The default interface with the default validator to use if not overridden
  13. interface DefaultPluginConfig extends PluginConfig {
  14. validator?: typeof EmailValidator;
  15. }
  16. const definePlugin = &lt;T extends PluginConfig = DefaultPluginConfig&gt;({
  17. validator = EmailValidator,
  18. }: T) =&gt; {
  19. return validator.parse({});
  20. };
  21. const test = definePlugin({});
  22. test.email; // Object has the correct type EmailValidator
  23. // Create a custom validator
  24. const CustomValidator = z.object({
  25. email: z.string(),
  26. username: z.string(),
  27. });
  28. // Create a custom interface to use as generic
  29. interface CustomConfig extends PluginConfig {
  30. validator?: typeof CustomValidator;
  31. }
  32. const test2 = definePlugin&lt;CustomConfig&gt;({
  33. validator: CustomValidator,
  34. });
  35. test2.username; // Object has the correct type CustomValidator
  36. 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:

  1. import { z, ZodType } from &quot;zod&quot;;
  2. // Create the default validator
  3. export const EmailValidator = z.object({
  4. email: z.string().default(&quot;&quot;)
  5. });
  6. // The default interface the generic should at least implement
  7. interface PluginConfig&lt;T extends ZodType = typeof EmailValidator&gt; {
  8. validator?: T;
  9. }
  10. const definePlugin = &lt;
  11. T extends PluginConfig = PluginConfig&lt;typeof EmailValidator&gt;,
  12. R = T extends PluginConfig&lt;infer V&gt; ? V : ZodType
  13. &gt;({
  14. validator = EmailValidator
  15. }: T): R extends ZodType&lt;infer P&gt; ? P : never =&gt; {
  16. return validator.parse({}) as any;
  17. };
  18. const test = definePlugin({});
  19. test.email; // Object has the correct type EmailValidator
  20. const CustomValidator = z.object({
  21. email: z.string().default(&quot;&quot;),
  22. username: z.string().default(&quot;&quot;)
  23. });
  24. type CustomConfig = PluginConfig&lt;typeof CustomValidator&gt;;
  25. const test2 = definePlugin&lt;CustomConfig&gt;({
  26. validator: CustomValidator
  27. });
  28. 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

  1. 基本解决方案是 z.infer
  1. const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
  2. validator = EmailValidator
  3. }: T): z.infer<NonNullable<T['validator']>> => {
  4. return validator.parse({})
  5. }

  1. 重载是处理调用边缘情况的最佳方法,因为它们具有推理尝试的顺序。我建议使用它们。
  1. function definePlugin({validator}: DefaultPluginConfig): z.infer<NonNullable<DefaultPluginConfig['validator']>>;
  2. function definePlugin<T extends PluginConfig>({validator}: T): z.infer<NonNullable<T['validator']>>;
  3. function definePlugin({validator = EmailValidator}: PluginConfig) {
  4. return validator.parse({})
  5. }
英文:
  1. The basic solution is to z.infer
  1. const definePlugin = &lt;T extends PluginConfig = DefaultPluginConfig&gt;({
  2. validator = EmailValidator
  3. }: T): z.infer&lt;NonNullable&lt;T[&#39;validator&#39;]&gt;&gt; =&gt; {
  4. return validator.parse({})
  5. }

This will work fine with provided generic parameter, but will fail to provide a default


  1. Overloads are the best way to work with call corner cases, as they have order of inference tries. I would recommend to use them.
  1. function definePlugin({validator}: DefaultPluginConfig): z.infer&lt;NonNullable&lt;DefaultPluginConfig[&#39;validator&#39;]&gt;&gt;;
  2. function definePlugin&lt;T extends PluginConfig&gt;({validator}: T): z.infer&lt;NonNullable&lt;T[&#39;validator&#39;]&gt;&gt;;
  3. function definePlugin({validator = EmailValidator}: PluginConfig) {
  4. return validator.parse({})
  5. }

huangapple
  • 本文由 发表于 2023年6月8日 18:04:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76430738.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定