# TypeScript

> This document is the Markdown version of [formisch.dev/react-native/guides/typescript/](https://formisch.dev/react-native/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](/react-native/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, handleSubmit, useForm } from '@formisch/react-native';
import { Button, View } from 'react-native';
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 LoginScreen() {
  const loginForm = useForm({
    schema: LoginSchema,
  });

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

  const submitForm = handleSubmit(loginForm, (output) => {
    // output is fully typed based on LoginSchema
    console.log(output.email); // ✓ Type-safe
    console.log(output.username); // ✗ TypeScript error
  });

  return (
    <View>
      {/* Form fields */}
      <Button title="Login" onPress={submitForm} />
    </View>
  );
}
```

### 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, handleSubmit, useForm } from '@formisch/react-native';
import { Button, TextInput, View } from 'react-native';
import * as v from 'valibot';

const ProfileSchema = v.object({
  age: v.pipe(
    v.string(), // Input type: string (from TextInput)
    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 ProfileScreen() {
  const profileForm = useForm({
    schema: ProfileSchema,
  });

  const submitForm = handleSubmit(profileForm, (output) => {
    // output is: { age: number; birthDate: Date }
    console.log(output.age); // number
    console.log(output.birthDate); // Date
  });

  return (
    <View>
      <Field of={profileForm} path={['age']}>
        {(field) => (
          // field.input is string
          <TextInput
            {...field.props}
            value={field.input}
            keyboardType="numeric"
          />
        )}
      </Field>

      <Field of={profileForm} path={['birthDate']}>
        {(field) => (
          // field.input is string
          <TextInput
            {...field.props}
            value={field.input}
            placeholder="YYYY-MM-DD"
          />
        )}
      </Field>

      <Button title="Submit" onPress={submitForm} />
    </View>
  );
}
```

### 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 of={userForm} path={['profile', 'email']} />

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

## Type-safe props

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

```tsx
import { type FormStore, handleSubmit, useForm } from '@formisch/react-native';
import { Button, View } from 'react-native';
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 LoginScreen() {
  const loginForm = useForm({
    schema: LoginSchema,
  });

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

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

function FormContent(props: FormContentProps) {
  const submitForm = handleSubmit(props.of, (output) => console.log(output));

  return (
    <View>
      {/* Form fields */}
      <Button title="Login" onPress={submitForm} />
    </View>
  );
}
```

## Generic field components

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

```tsx
import { type FormStore, useField } from '@formisch/react-native';
import type { FunctionComponent } from 'react';
import { Text, TextInput, View } from 'react-native';
import * as v from 'valibot';

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

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

  return (
    <View>
      <Text>Email</Text>
      <TextInput
        {...field.props}
        value={field.input}
        autoCapitalize="none"
        keyboardType="email-address"
      />
      {field.errors && <Text>{field.errors[0]}</Text>}
    </View>
  );
};
```

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/react-native`. You can find all available types in our [API reference](/react-native/api/).

- [`FormStore`](/react-native/api/FormStore.md) - The form store
  type
- [`FieldStore`](/react-native/api/FieldStore.md) - The field
  store type
- [`FieldArrayStore`](/react-native/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 that receive the validated output
