# TypeScript

> This document is the Markdown version of [formisch.dev/honox/guides/typescript/](https://formisch.dev/honox/guides/typescript/). For the complete documentation index, see [llms.txt](https://formisch.dev/llms.txt).

Since the library is written in TypeScript and we put a lot of emphasis on the development experience, you can expect maximum TypeScript support. Types are automatically inferred from your [Valibot schemas](/honox/guides/define-your-form.md), providing type safety throughout your forms.

## Type inference

Formisch uses Valibot's type inference to automatically derive TypeScript types from your schemas. You don't need to define separate types—they're inferred automatically.

```tsx
import { Field, Form, useForm } from '@formisch/honox';
import * as v from 'valibot';

const LoginSchema = v.object({
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

export default function LoginPage() {
  const loginForm = useForm({
    schema: LoginSchema,
  });

  // TypeScript knows the form structure from the schema
  // loginForm is of type FormStore<typeof LoginSchema>

  return (
    <Form
      of={loginForm}
      onSubmit={(output, event) => {
        // output is fully typed based on LoginSchema
        console.log(output.email); // ✓ Type-safe
        console.log(output.username); // ✗ TypeScript error
      }}
    >
      {/* Form fields */}
    </Form>
  );
}
```

### Input and output types

Valibot schemas can have different input and output types when using transformations. Formisch provides proper typing for both.

```tsx
import { Field, Form, useForm } from '@formisch/honox';
import * as v from 'valibot';

const ProfileSchema = v.object({
  age: v.pipe(
    v.string(), // Input type: string (from HTML input)
    v.transform((input) => Number(input)), // Output type: number
    v.number()
  ),
  birthDate: v.pipe(
    v.string(), // Input type: string (ISO date string)
    v.transform((input) => new Date(input)), // Output type: Date
    v.date()
  ),
});

export default function ProfilePage() {
  const profileForm = useForm({
    schema: ProfileSchema,
  });

  return (
    <Form
      of={profileForm}
      onSubmit={(output, event) => {
        // output is: { age: number; birthDate: Date }
        console.log(output.age); // number
        console.log(output.birthDate); // Date
      }}
    >
      <Field of={profileForm} path={['age']}>
        {(field) => (
          // field.input is string
          <input {...field.props} value={field.input} type="text" />
        )}
      </Field>

      <Field of={profileForm} path={['birthDate']}>
        {(field) => (
          // field.input is string
          <input {...field.props} value={field.input} type="date" />
        )}
      </Field>

      <button type="submit">Submit</button>
    </Form>
  );
}
```

### Type-safe paths

Field paths are fully type-checked. TypeScript will provide autocompletion and catch invalid paths at compile time.

```tsx
const UserSchema = v.object({
  profile: v.object({
    name: v.object({
      first: v.string(),
      last: v.string(),
    }),
    email: v.string(),
  }),
});

const userForm = useForm({ schema: UserSchema });

// ✓ Valid paths - TypeScript provides autocompletion
<Field of={userForm} path={['profile', 'name', 'first']}>{() => <>{/* ... */}</>}</Field>
<Field of={userForm} path={['profile', 'email']}>{() => <>{/* ... */}</>}</Field>

// ✗ Invalid paths - TypeScript error
<Field of={userForm} path={['profile', 'age']}>{() => <>{/* ... */}</>}</Field>
<Field of={userForm} path={['username']}>{() => <>{/* ... */}</>}</Field>
```

## Type-safe props

To pass your form to another component via props, you can use the [`FormStore`](/honox/api/FormStore.md) type along with your schema type to get full type safety.

```tsx
import { Form, type FormStore, useForm } from '@formisch/honox';
import * as v from 'valibot';

const LoginSchema = v.object({
  email: v.pipe(v.string(), v.email()),
  password: v.pipe(v.string(), v.minLength(8)),
});

export default function LoginPage() {
  const loginForm = useForm({
    schema: LoginSchema,
  });

  return <FormContent of={loginForm} />;
}

type FormContentProps = {
  of: FormStore<typeof LoginSchema>;
};

function FormContent(props: FormContentProps) {
  return (
    <Form of={props.of} onSubmit={(output, event) => console.log(output)}>
      {/* Form fields */}
    </Form>
  );
}
```

## Generic field components

You can create generic field components with proper TypeScript typing using the [`FormStore`](/honox/api/FormStore.md) type with Valibot's `GenericSchema`.

```tsx
import { type FormStore, useField } from '@formisch/honox';
import type { FC } from 'hono/jsx';
import * as v from 'valibot';

type EmailInputProps = {
  of: FormStore<v.GenericSchema<{ email: string }>>;
};

const EmailInput: FC<EmailInputProps> = (props) => {
  const field = useField(props.of, { path: ['email'] });

  return (
    <div>
      <label>
        Email
        <input {...field.props} value={field.input} type="email" />
      </label>
      {field.errors && <div>{field.errors[0]}</div>}
    </div>
  );
};
```

The `v.GenericSchema<{ email: string }>` type ensures that the form passed to `EmailInput` must have an `email` field of type `string`. TypeScript will catch any type mismatches at compile time.

## Available types

Most types you need can be imported from `@formisch/honox`. You can find all available types in our [API reference](/honox/api/).

- [`FormStore`](/honox/api/FormStore.md) - The form store type
- [`FieldStore`](/honox/api/FieldStore.md) - The field store type
- [`FieldArrayStore`](/honox/api/FieldArrayStore.md) - The field
  array store type
- [`ValidPath`](/core/api/ValidPath.md) - Type for valid field
  paths
- [`ValidArrayPath`](/core/api/ValidArrayPath.md) - Type for
  valid array field paths
- [`FormSchema`](/core/api/FormSchema.md) - Form schema type
  required at the root
- [`Schema`](/core/api/Schema.md) - Base schema type from Valibot
- [`SubmitHandler`](/core/api/SubmitHandler.md) - Type for submit
  handlers without the event
- [`SubmitEventHandler`](/core/api/SubmitEventHandler.md) - Type
  for submit handlers with the event
