网站搜索

typescript - 在 Sveltekit 中,你能返回两个失败的 ActionResults 吗?


我在我的 +page.server.ts 文件中有一个表单操作,如果 zod 模式验证失败,我会返回失败,这个是可行的。但是,当我在 PocketBase 抛出邮箱已被使用的错误时返回另一个失败,我会得到这个类型错误,除了邮箱之外,每个字段都会出现。

Typescript Warning
Property 'password' does not exist on type '{ password?: string[] | undefined; passwordConfirm?: string[] | undefined; email?: string[] | undefined; name?: string[] | undefined; terms?: string[] | undefined; } | { email: string[]; }'.
  Property 'password' does not exist on type '{ email: string[]; }'

问题:

  1. 是否有可能从 +page.server.ts 文件发送多个失败?
  2. 我该如何正确修复这些类型警告?
  3. 我这样做对吗?如果不对,我该如何改正?
+page.svelte
...
    <input id="password"
           name="password"
           type="password"
           placeholder="Password"
           class="input input-bordered w-full" 
    />
    <label class="label" for="password">
        {#if form?.errors?.password}
                       <!--^^^Type Error Here-->
            <span class="label-text-alt text-error">{form?.errors?.password[0]}</span>
                                                               <!--^^^Type Error Here-->
        {/if}
    </label>
...
//Type of errors coming from +page.server.ts into +page.svelte
(property) errors: {
    password?: string[] | undefined;
    passwordConfirm?: string[] | undefined;
    email?: string[] | undefined;
    name?: string[] | undefined;
    terms?: string[] | undefined;
} | {
    email: string[];
} | undefined
+page.server.ts
export const actions: Actions = {
    default: async ({ locals, request }) => {
        const formData = Object.fromEntries(await request.formData()) as {
            email: string;
            password: string;
            passwordConfirm: string;
            name: string;
            terms: string;
        };

        const result = registerSchema.safeParse(formData);

        // eslint-disable-next-line @typescript-eslint/no-unused-vars
        const { password, passwordConfirm, ...rest } = formData;

        if (!result.success) {
            const { fieldErrors: errors } = result.error.flatten();
            const data = {
                data: rest,
                errors
            };
            return fail(400, data);
        }

        try {
            await locals.pb.collection('users').create(formData);
            await locals.pb.collection('users').authWithPassword(formData.email, formData.password);
        } catch (e) {
            if (e instanceof ClientResponseError) {
                const data = {
                    data: rest,
                    errors: {
                          email: ['Email already exists']
                    }
                };
                return fail(400, data);
            }
            throw error(500, 'Something went wrong');
        }
        throw redirect(303, '/');
    }
};

答案#1

你只能返回一个失败结果。

如果你保留这两个失败的结果,但想解决 TS 问题,你可以声明一个类型,并断言电子邮件检查的错误符合该类型。例如。

const formData = Object.fromEntries(await request.formData()) as {
    email: string;
    password: string;
};

const schema = z.object({
    email: z.string().email(),
    password: z.string().min(8),
});

// This matches errors of Zob validation
type Errors = Partial<Record<keyof typeof formData, string[]>>;

const result = schema.safeParse(formData);
if (!result.success) {
    return fail(400, {
        errors: result.error.flatten().fieldErrors,
    });
}

if (formData.email == '[email ') {
    return fail(400, {
        errors: {
            email: ['already exists'],
        } as Errors, // This ensures that the errors type is the same
    });
}

return { message: 'ok' };

请注意,as Errors 不会捕获多余的属性。因此,如果你拼写错误一个属性,你不会得到编译器错误。

errors 的类型是从函数可能返回的所有值中推导出来的,因此如果你首先将所有错误合并为一个对象并且只返回一个失败结果,类型不匹配应该已经通过合并处理了。

如果 Zod 支持异步验证,你就可以用那种方式进行完整验证。现在你可能必须手动进行,或者使用对象合并库。