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

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

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 &#39;zod&#39;

// Create de default validator
export const EmailValidator = z.object({
  email: z
    .string({ required_error: &#39;auth.validation.email&#39; })
    .email({ message: &#39;auth.validation.email_format&#39; })
})

// 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 = &lt;T extends PluginConfig = DefaultPluginConfig&gt;({
  validator = EmailValidator
}: T) =&gt; {
  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&lt;CustomConfig&gt;({
  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

以下是翻译好的部分:

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

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

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

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

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

你的代码应该如下所示:

import { z } from &#39;zod&#39;;

// 创建默认验证器
export const EmailValidator = z.object({
  email: z
    .string({ required_error: &#39;auth.validation.email&#39; })
    .email({ message: &#39;auth.validation.email_format&#39; }),
});

// 泛型应该至少实现的默认接口
interface PluginConfig {
  validator?: z.Schema&lt;any&gt;;
}

// 带有默认验证器的默认接口,如果未被覆盖,则使用此验证器
interface DefaultPluginConfig extends PluginConfig {
  validator?: typeof EmailValidator;
}

const definePlugin = &lt;T extends PluginConfig = DefaultPluginConfig&gt;({
  validator = EmailValidator,
}: T) =&gt; {
  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&lt;CustomConfig&gt;({
  validator: CustomValidator,
});
test2.username; // 对象具有正确的类型CustomValidator
test2.email; // 对象具有正确的类型CustomValidator(来自CustomValidator)

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

更新后的代码(最终版)

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

import { z, ZodType } from &quot;zod&quot;;

// 创建默认验证器
export const EmailValidator = z.object({
  email: z.string().default(&quot;&quot;)
});

// 泛型至少应该实现的默认接口
interface PluginConfig&lt;T extends ZodType = typeof EmailValidator&gt; {
  validator?: T;
}

const definePlugin = &lt;
  T extends PluginConfig = PluginConfig&lt;typeof EmailValidator&gt;,
  R = T extends PluginConfig&lt;infer V&gt; ? V : ZodType
&gt;({
  validator = EmailValidator
}: T): R extends ZodType&lt;infer P&gt; ? P : never =&gt; {
  return validator.parse({}) as any;
};

const test = definePlugin({});
test.email; // 对象具有正确的类型EmailValidator

const CustomValidator = z.object({
  email: z.string().default(&quot;&quot;),
  username: z.string().default(&quot;&quot;)
});

type CustomConfig = PluginConfig&lt;typeof CustomValidator&gt;;

const test2 = definePlugin&lt;CustomConfig&gt;({
  validator: CustomValidator
});

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:

import { z } from &#39;zod&#39;;

// Create the default validator
export const EmailValidator = z.object({
  email: z
    .string({ required_error: &#39;auth.validation.email&#39; })
    .email({ message: &#39;auth.validation.email_format&#39; }),
});

// The default interface the generic should at least implement
interface PluginConfig {
  validator?: z.Schema&lt;any&gt;;
}

// The default interface with the default validator to use if not overridden
interface DefaultPluginConfig extends PluginConfig {
  validator?: typeof EmailValidator;
}

const definePlugin = &lt;T extends PluginConfig = DefaultPluginConfig&gt;({
  validator = EmailValidator,
}: T) =&gt; {
  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&lt;CustomConfig&gt;({
  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 &quot;zod&quot;;

// Create the default validator
export const EmailValidator = z.object({
  email: z.string().default(&quot;&quot;)
});

// The default interface the generic should at least implement
interface PluginConfig&lt;T extends ZodType = typeof EmailValidator&gt; {
  validator?: T;
}

const definePlugin = &lt;
  T extends PluginConfig = PluginConfig&lt;typeof EmailValidator&gt;,
  R = T extends PluginConfig&lt;infer V&gt; ? V : ZodType
&gt;({
  validator = EmailValidator
}: T): R extends ZodType&lt;infer P&gt; ? P : never =&gt; {
  return validator.parse({}) as any;
};

const test = definePlugin({});
test.email; // Object has the correct type EmailValidator

const CustomValidator = z.object({
  email: z.string().default(&quot;&quot;),
  username: z.string().default(&quot;&quot;)
});

type CustomConfig = PluginConfig&lt;typeof CustomValidator&gt;;

const test2 = definePlugin&lt;CustomConfig&gt;({
  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

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

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

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.
function definePlugin({validator}: DefaultPluginConfig): z.infer&lt;NonNullable&lt;DefaultPluginConfig[&#39;validator&#39;]&gt;&gt;;
function definePlugin&lt;T extends PluginConfig&gt;({validator}: T): z.infer&lt;NonNullable&lt;T[&#39;validator&#39;]&gt;&gt;;
function definePlugin({validator = EmailValidator}: PluginConfig) {
  return validator.parse({})
}

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:

确定