Búsqueda de sitios web

javascript - ¿Cómo anular una interfaz en una función y mantener el tipo de retorno correcto?


Tengo una función definePlugin que acepta un objeto que implementa la interfaz PluginConfig, con un RequestValidator por defecto. Estoy tratando de sobrescribir esta interfaz, proporcionando un validador personalizado mientras aseguro que el valor devuelto tenga el tipo correcto. Sin embargo, mi implementación actual hace que vueAuth devuelto tenga el tipo any. ¿Cómo puedo sobrescribir la interfaz manteniendo el tipo de retorno correcto? Aquí está mi código actual:

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

¿Qué cambios debería hacer para asegurarme de que definePlugin tenga el tipo correcto al proporcionar un validador personalizado?

Respuesta #1

Hay algunos problemas en tu código que causan inconvenientes:

  1. Has definido PluginConfig como propiedad z.ZodType que es un tipo, no un valor. Puedes modificarlo así:

    interfaz PluginConfig { validador?: z.Schema; }

  2. En tu código, tu interfaz DefaultPluginConfig no está extendiendo correctamente PluginConfig. Puedes cambiarlo así:

    interfaz DefaultPluginConfig extiende PluginConfig { validator?: typeof EmailValidator; }

Tu código se vería así:

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)

Déjame saber si aún enfrentas algún problema.

CÓDIGO ACTUALIZADO (FINAL):

Según lo discutido en el chat, la respuesta correcta es:

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);

Así que aquí Generic V y P se utilizan para capturar los tipos de PluginConfig y ZodType respectivamente.

Aquí,
V - representa los tipos inferidos de la propiedad validator en PluginConfig
P - representa el tipo inferido de ZodType

Estos genéricos V y P permiten que la función maneje diferentes configuraciones de validadores y devuelva un tipo inferido adecuado basado en ellas.

Respuesta #2

  1. La solución básica es z.infer
const definePlugin = <T extends PluginConfig = DefaultPluginConfig>({
  validator = EmailValidator
}: T): z.infer<NonNullable<T['validator']>> => {
  return validator.parse({})
}

Esto funcionará bien con el parámetro genérico proporcionado, pero fallará al proporcionar un valor predeterminado


  1. Las sobrecargas son la mejor manera de trabajar con casos límite de llamadas, ya que tienen un orden de intentos de inferencia. Recomendaría usarlas.
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({})
}