带有泛型参数的 TypeScript zod 类型安全函数

问题描述 投票:0回答:1

我正在使用 zod 来验证 TypeScript 项目中的表单。 我有一个函数,对于给定的表单模式,可以采用表单值和另一个函数

requestFunc
,该函数将表单值作为输入。

我想让这个函数类型安全,以确保我不会混淆不同的形式。

所以我尝试使用 TypeScript 的 Generics 声明,如下所示:

function schemaTest<T extends z.ZodSchema>(
  formData: z.infer<T>,
  requestFunc: (formData: z.infer<T>) => void
): void {}

但它没有按预期工作,我永远不会收到任何类型错误,除非我定义两个具有不同命名属性的表单并且我像这样使用该函数:

schemaTest<typeof schemaA>(
  schemaA.parse({
    email: '[email protected]',
    password: '123456'
  }),
  (formData: z.infer<typeof schemaC>) => {
    console.log(formData);
  }
);

这是一个测试不同场景的完整示例,我添加了显示类型或错误的注释:

import { z } from 'zod';

// The test function
function schemaTest<T extends z.ZodSchema>(
  formData: z.infer<T>,
  requestFunc: (formData: z.infer<T>) => void
): void {}

// Example schemas
const schemaA = z.object({
  email: z.string().email('Invalid email address'),
  password: z.string().min(6, 'Password must be at least 6 characters long')
});

const schemaB = z.object({
  email: z.string().email('Invalid email address') // Changed property name
});

const schemaC = z.object({
  othername: z.string().email('Invalid email address') // Changed property name
});

// function schemaTest<z.ZodType<any, z.ZodTypeDef, any>>(formData: any, requestFunc: (formData: any) => void): void
schemaTest(
  schemaA.parse({
    email: '[email protected]',
    password: '123456'
  }),
  (formData: z.infer<typeof schemaB>) => {
    console.log(formData);
  }
);


// function schemaTest<z.ZodObject<{
//     email: z.ZodString;
//     password: z.ZodString;
// }, "strip", z.ZodTypeAny, {
//     email: string;
//     password: string;
// }, {
//     email: string;
//     password: string;
// }>>(formData: {
//     email: string;
//     password: string;
// }, requestFunc: (formData: {
//     email: string;
//     password: string;
// }) => void): void
schemaTest<typeof schemaA>(
  schemaA.parse({
    email: '[email protected]',
    password: '123456'
  }),
  (formData: z.infer<typeof schemaB>) => {
    console.log(formData);
  }
);


// Argument of type '(formData: z.infer<typeof schemaC>) => void' is not assignable to parameter of type '(formData: { email: string; password: string; }) => void'.
//  Types of parameters 'formData' and 'formData' are incompatible.
//  Property 'othername' is missing in type '{ email: string; password: string; }' but required in type '{ othername: string; }'.ts(2345)
schemaTest<typeof schemaA>(
  schemaA.parse({
    email: '[email protected]',
    password: '123456'
  }),
  (formData: z.infer<typeof schemaC>) => {
    console.log(formData);
  }
);

// function schemaTest<z.ZodType<any, z.ZodTypeDef, any>>(formData: any, requestFunc: (formData: any) => void): void
schemaTest(
  schemaA.parse({
    email: '[email protected]',
    password: '123456'
  }),
  (formData: z.infer<typeof schemaC>) => {
    console.log(formData);
  }
);

typescript forms types zod
1个回答
0
投票

问题

这里的问题是 TypeScript 无法根据传递给函数的参数正确推断类型

T

原功能实现:

function schemaTest<T extends z.ZodSchema>(
  formData: z.infer<T>,
  requestFunc: (formData: z.infer<T>) => void
): void {}

z.infer<T>
实用程序类型从 Zod 模式
T
中提取推断类型。然而,一旦推断出来,结果类型就不再携带有关
T
本身的任何信息。它变成了具体类型(例如,{ email: string;password: string; }),失去了与原始模式的关联
T
。 TypeScript 无法根据推断类型对泛型类型
T
进行逆向工程。

解决方案

您需要让 TypeScript 知道

T
的样子。可以通过两种方式完成:

选项1

将模式本身添加为函数参数,以便 TypeScript 可以从中推断出

T

function schemaTest<T extends z.ZodTypeAny>(
  schema: T,
  formData: z.infer<T>,
  requestFunc: (formData: z.infer<T>) => void
): void {}

//Example
schemaTest(
  schemaA,
  schemaA.parse({
    email: '[email protected]',
    password: '123456'
  }),
  (formData) => {
    console.log(formData);
  }
);

选项2

您可以修改函数以接受携带通用类型信息的高阶函数,而不是将架构作为参数传递。

function schemaTest<T extends z.ZodTypeAny>(
  requestFunc: (formData: z.infer<T>) => void
): (formData: z.infer<T>) => void {
  return (formData: z.infer<T>) => {
    requestFunc(formData);
  };
}

//Example
const handleSchemaA = schemaTest<typeof schemaA>((formData) => {
  console.log(formData);
});

handleSchemaA(
  schemaA.parse({
    email: '[email protected]',
    password: '123456'
  })

);
© www.soinside.com 2019 - 2024. All rights reserved.