1

I am currently looking for a way to add types to the custom events emitted from the custom action to be used in use directive I am creating. Here is the snippet.

import { z } from "zod";

const acceptedNodeNames = [
  //
  "INPUT",
  "SELECT",
  "TEXTAREA",
];

// prettier-ignore
type FormField =
  | HTMLInputElement
  | HTMLSelectElement
  | HTMLTextAreaElement;

type SubmitConfig = {
  schema: {
    [key: string]: z.Schema;
  };
};

export function form(node: HTMLFormElement, { schema }: SubmitConfig) {
  // store fields that needs to be validated in array
  const fields = Array.from(node.children).filter(
    (child) => acceptedNodeNames.includes(child.nodeName) && (child as FormField).name in schema,
  ) as FormField[];

  function handleInput(this: FormField) {
    const { name, value } = this;

    const result = schema[name].safeParse(value);

    if (result.success) {
      this.removeAttribute("aria-invalid");
    } else {
      this.setAttribute("aria-invalid", "");
    }
  }

  // attach field listeners
  for (const field of fields) {
    field.addEventListener("input", handleInput);
  }

  function handleSubmit(e: SubmitEvent) {
    e.preventDefault();

    const input = fields.reduce(
      (obj, field) => ({
        ...obj,
        [field.name]: field.value,
      }),
      {},
    );

    const result = z.object(schema).safeParse(input);

    if (result.success) {
      const event = new CustomEvent("okay", { detail: { ...result } });
      node.dispatchEvent(event);
    } else {
      const errors = JSON.parse(result.error.message);

      for (const error of errors) {
        const field = fields.find(({ name }) => name === error.path[0]);

        if (!field) continue;

        field.setAttribute("aria-invalid", "");
      }

      const event = new CustomEvent("fail", { detail: { errors } });
      node.dispatchEvent(event);
    }
  }

  // attach form submit handler
  node.addEventListener("submit", handleSubmit);

  return {
    destroy() {
      node.removeEventListener("submit", handleSubmit);

      for (const field of fields) {
        field.removeEventListener("input", handleInput);
      }
    },
  };
}

It basically just validates form field and uses zod underneath. This is how I would like to use it.

<form
  novalidate
  method="POST"
  use:form={{
    schema: {
      email: z.string().email(),
    },
  }}
  on:okay={(e) => {
    console.log(e.detail);
  }}
  on:fail={(e) => {
    console.log(e.detail);
  }}
>
...
</form>

This code is already working, but my only problem is that it doesn't have types and it got some annoying squiggly lines in vscode. Is there a way at the moment to declare a type to those custom events similar to adding types in svelte components like so

 interface $$Events {
    change: CustomEvent<{
      page: number;
      pageSize: number;
    }>;
  }

Thanks!

JP Calvo
  • 173
  • 1
  • 2
  • 14
  • Does this answer your question? [Svelte (svelte-kit) type custom action event with typescript](https://stackoverflow.com/questions/73025100/svelte-svelte-kit-type-custom-action-event-with-typescript) – H.B. Feb 07 '23 at 16:39
  • Thanks @H.B. will check this out later today – JP Calvo Feb 08 '23 at 04:38

0 Answers0