> ## Documentation Index
> Fetch the complete documentation index at: https://inertiajs-vi.tuantq.online/llms.txt
> Use this file to discover all available pages before exploring further.

# TypeScript

Inertia hỗ trợ TypeScript ở mức first-class. Bạn có thể cấu hình kiểu toàn cục bằng declaration merging và truyền generic vào hook cũng như phương thức router để có props, form và quản lý state type-safe.

## Sử dụng pnpm

Do cơ chế cô lập dependency nghiêm ngặt của pnpm, `@inertiajs/core` không truy cập được tại `node_modules/@inertiajs/core`. Thay vào đó, package nằm lồng trong `.pnpm/`, khiến TypeScript module augmentation không thể resolve module.

Bạn có thể khắc phục bằng cách cấu hình pnpm để [hoist package](https://pnpm.io/settings#publichoistpattern). Thêm cấu hình sau vào file `.npmrc` rồi chạy `pnpm install`.

```ini theme={null}
public-hoist-pattern[]=@inertiajs/core
```

Hoặc bạn có thể thêm `@inertiajs/core` làm dependency trực tiếp của project.

```bash theme={null}
pnpm add @inertiajs/core
```

## Cấu hình toàn cục

Bạn có thể cấu hình type của Inertia trên toàn cục bằng cách augment interface `InertiaConfig` trong module `@inertiajs/core`. Việc này thường được thực hiện trong file `global.d.ts` ở thư mục root hoặc thư mục `types` của project.

```ts theme={null}
// global.d.ts
import '@inertiajs/core'

declare module "@inertiajs/core" {
  export interface InertiaConfig {
    sharedPageProps: {
      auth: { user: { id: number; name: string } | null };
      appName: string;
    };
    flashDataType: {
      toast?: { type: "success" | "error"; message: string };
    };
    errorValueType: string[];
    layoutProps: {
      title: string;
      showSidebar: boolean;
    };
    namedLayoutProps: {
      app: { title: string; theme: "light" | "dark" };
      content: { padding: string; maxWidth: string };
    };
  }
}
```

<Note>
  Câu lệnh `import` (hoặc `export {}`) là bắt buộc để biến file này thành module. Nếu thiếu nó, `declare module` sẽ thay thế định nghĩa module thay vì augment module. `tsconfig.json` cũng phải include các file `.d.ts`, vì vậy hãy đảm bảo mảng `include` có pattern như `"resources/js/**/*.d.ts"`.
</Note>

### Shared page props

Tùy chọn `sharedPageProps` định nghĩa type dữ liệu được [share](/v3/data-props/shared-data) với mọi page. Với cấu hình này, `page.props.auth` và `page.props.appName` có type đúng ở mọi nơi.

```ts theme={null}
sharedPageProps: {
    auth: { user: { id: number; name: string } | null }
    appName: string
}
```

### Flash data

Tùy chọn `flashDataType` định nghĩa type [flash data](/v3/data-props/flash-data) trong ứng dụng.

```ts theme={null}
flashDataType: {
    toast?: { type: 'success' | 'error'; message: string }
}
```

### Giá trị lỗi

Mặc định validation error value có type `string`. Bạn có thể cấu hình TypeScript để mong đợi mảng cho [nhiều lỗi trên mỗi field](/v3/the-basics/validation#multiple-errors-per-field).

```ts theme={null}
errorValueType: string[]
```

### Layout Props

Tùy chọn `layoutProps` khai báo type dữ liệu `setLayoutProps()` nhận. `namedLayoutProps` khai báo type dữ liệu `setLayoutProps('name', props)` nhận, key theo layout name.

```ts theme={null}
layoutProps: {
    title: string
    showSidebar: boolean
}
namedLayoutProps: {
    app: { title: string; theme: 'light' | 'dark' }
    content: { padding: string; maxWidth: string }
}
```

Với cấu hình này, `setLayoutProps({ title: 'Dashboard' })` được type-check, còn `setLayoutProps('app', { theme: 'dark' })` validate cả layout name lẫn props.

Bạn cũng có thể truyền generic type parameter trực tiếp vào `setLayoutProps` để khai báo type ad-hoc mà không cấu hình interface `InertiaConfig` toàn cục.

```ts theme={null}
setLayoutProps<{ custom: string }>({ custom: 'value' })
setLayoutProps<{ collapsed: boolean }>('sidebar', { collapsed: true })
```

<Note>
  Phiên bản tiếp theo của [Laravel
  Wayfinder](https://github.com/laravel/wayfinder/tree/next) có thể tự động
  sinh các type này bằng cách phân tích ứng dụng Laravel. Nó
  sinh TypeScript type cho shared props, page props, form request và
  Eloquent model. Phiên bản này hiện ở giai đoạn beta.
</Note>

## Page component

Bạn có thể khai báo type cho kết quả `import.meta.glob` để tăng type safety khi resolve page component.

<CodeGroup>
  ```ts Vue icon="vuejs" theme={null}
  import { createInertiaApp } from "@inertiajs/vue3";
  import type { DefineComponent } from "vue";

  createInertiaApp({
    resolve: (name) => {
      const pages = import.meta.glob<DefineComponent>("./Pages/**/*.vue");
      return pages[`./Pages/${name}.vue`]();
    },
    // ...
  });
  ```

  ```tsx React icon="react" theme={null}
  import { createInertiaApp, type ResolvedComponent } from "@inertiajs/react";

  createInertiaApp({
    resolve: (name) => {
      const pages = import.meta.glob<ResolvedComponent>("./Pages/**/*.tsx");
      return pages[`./Pages/${name}.tsx`]();
    },
    // ...
  });
  ```

  ```ts Svelte icon="s" theme={null}
  import { createInertiaApp, type ResolvedComponent } from "@inertiajs/svelte";

  createInertiaApp({
    resolve: (name) => {
      const pages = import.meta.glob<ResolvedComponent>("./Pages/**/*.svelte");
      return pages[`./Pages/${name}.svelte`]();
    },
    // ...
  });
  ```
</CodeGroup>

## Page props

Bạn có thể khai báo kiểu cho prop riêng của từng trang bằng cách truyền generic vào `usePage()`. Chúng được gộp với `sharedPageProps` toàn cục, nhờ đó bạn có autocomplete và type checking cho cả dữ liệu shared lẫn dữ liệu riêng của trang.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup lang="ts">
  import { usePage } from "@inertiajs/vue3";

  const page = usePage<{
    posts: { id: number; title: string }[];
  }>();
  </script>
  ```

  ```tsx React icon="react" theme={null}
  import { usePage } from "@inertiajs/react";

  export default function Posts() {
    const page = usePage<{
      posts: { id: number; title: string }[];
    }>();

    return (
      <ul>
        {page.props.posts.map((post) => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    );
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script lang="ts">
      import { usePage } from '@inertiajs/svelte'

      const page = usePage<{
          posts: { id: number; title: string }[]
      }>()
  </script>
  ```
</CodeGroup>

## Form helper

[Form helper](/v3/the-basics/forms#form-helper) nhận generic type parameter cho form data và error handling type-safe. Điều này cung cấp autocomplete cho form field/error và ngăn lỗi chính tả tên field.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup lang="ts">
  import { useForm } from "@inertiajs/vue3";

  const form = useForm<{
    name: string;
    email: string;
    company: { name: string };
  }>({
    name: "",
    email: "",
    company: { name: "" },
  });
  </script>
  ```

  ```tsx React icon="react" theme={null}
  import { useForm } from "@inertiajs/react";

  export default function CreateUser() {
    const form = useForm<{
      name: string;
      email: string;
      company: { name: string };
    }>({
      name: "",
      email: "",
      company: { name: "" },
    });

    return null;
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script lang="ts">
      import { useForm } from '@inertiajs/svelte'

      const form = useForm<{
          name: string
          email: string
          company: { name: string }
      }>({
          name: '',
          email: '',
          company: { name: '' },
      })
  </script>
  ```
</CodeGroup>

### Dữ liệu lồng nhau và mảng

Form type hỗ trợ đầy đủ object lồng nhau và mảng. Bạn có thể truy cập và cập nhật field lồng nhau bằng dot notation, còn error key sẽ tự động có kiểu tương ứng.

```ts theme={null}
import { useForm } from "@inertiajs/react";

const form = useForm<{
  user: { name: string; email: string };
  tags: { id: number; label: string }[];
}>({
  user: { name: "", email: "" },
  tags: [],
});
```

## Component Form

Component `<Form>` nhận generic type parameter cho slot prop type-safe. Trong React có thể truyền generic trực tiếp. Trong Vue và Svelte, bạn có thể dùng helper `createForm` để tạo typed form component.

<CodeGroup>
  ```tsx React icon="react" theme={null}
  import { Form } from "@inertiajs/react";

  interface UserForm {
    name: string;
    email: string;
  }

  export default function CreateUser() {
    return (
      <Form<UserForm> action="/users" method="post">
        {({ errors }) => (
          <>
            <input type="text" name="name" />
            {errors.name && <div>{errors.name}</div>}
            <button type="submit">Create User</button>
          </>
        )}
      </Form>
    );
  }
  ```

  ```vue Vue icon="vuejs" theme={null}
  <script setup lang="ts">
  import { createForm } from "@inertiajs/vue3";

  interface UserForm {
    name: string;
    email: string;
  }

  const TypedForm = createForm<UserForm>();
  </script>

  <template>
    <TypedForm action="/users" method="post" #default="{ errors }">
      <input type="text" name="name" />
      <div v-if="errors.name">{{ errors.name }}</div>
      <button type="submit">Create User</button>
    </TypedForm>
  </template>
  ```

  ```svelte Svelte icon="s" theme={null}
  <script lang="ts">
      import { createForm } from '@inertiajs/svelte'

      interface UserForm {
          name: string
          email: string
      }

      const TypedForm = createForm<UserForm>()
  </script>

  <TypedForm action="/users" method="post">
      {#snippet children({ errors })}
          <input type="text" name="name" />
          {#if errors.name}<div>{errors.name}</div>{/if}
          <button type="submit">Create User</button>
      {/snippet}
  </TypedForm>
  ```
</CodeGroup>

Generic cung cấp autocomplete và type checking cho object `errors`, `setError`, `clearErrors` và slot prop khác tham chiếu form field.

### useFormContext

Function `useFormContext()` cũng nhận generic type parameter, cung cấp quyền truy cập form context type-safe từ child component.

<CodeGroup>
  ```tsx React icon="react" theme={null}
  import { useFormContext } from "@inertiajs/react";

  const form = useFormContext<UserForm>();
  ```

  ```vue Vue icon="vuejs" theme={null}
  <script setup lang="ts">
  import { useFormContext } from "@inertiajs/vue3";

  const form = useFormContext<UserForm>();
  </script>
  ```

  ```svelte Svelte icon="s" theme={null}
  <script lang="ts">
      import { useFormContext } from '@inertiajs/svelte'

      const form = useFormContext<UserForm>()
  </script>
  ```
</CodeGroup>

## HTTP Helper

Hook [`useHttp`](/v3/the-basics/http-requests) nhận hai generic type parameter: form data type và default response type tùy chọn.

```ts theme={null}
import { useHttp } from "@inertiajs/react";

interface UserForm {
  name: string;
  email: string;
}

interface UserResponse {
  id: number;
  name: string;
}

const http = useHttp<UserForm, UserResponse>({ name: "", email: "" });
```

## Layout Callbacks

Type `LayoutCallback` cung cấp type safety cho [layout callback](/v3/the-basics/layouts#callback-props). Callback nhận props của page (được khai báo type qua cấu hình `sharedPageProps` toàn cục) và nên trả về layout definition hợp lệ.

<CodeGroup>
  ```tsx React icon="react" theme={null}
  import type { LayoutCallback } from "@inertiajs/react";
  import Layout from "./Layout";

  const Profile = () => <h1>Profile</h1>;

  const callback: LayoutCallback = (props) => {
    return [Layout, { title: props.auth.user?.name }];
  };

  Profile.layout = callback;

  export default Profile;
  ```

  ```vue Vue icon="vuejs" theme={null}
  <script lang="ts">
  import Layout from "./Layout.vue";
  import type { LayoutCallback } from "@inertiajs/vue3";

  const callback: LayoutCallback = (props) => {
    return [Layout, { title: props.auth.user?.name }];
  };

  export default {
    layout: callback,
  };
  </script>
  ```

  ```svelte Svelte icon="s" theme={null}
  <script module lang="ts">
      import Layout from './Layout.svelte'
      import type { LayoutCallback } from '@inertiajs/svelte'

      export const layout: LayoutCallback = (props) => {
          return [Layout, { title: props.auth.user?.name }]
      }
  </script>
  ```
</CodeGroup>

## Ghi nhớ state

Hook `useRemember` nhận generic type parameter để lưu local state an toàn kiểu, cung cấp autocomplete và đảm bảo giá trị khớp với kiểu mong đợi.

<CodeGroup>
  ```vue Vue icon="vuejs" theme={null}
  <script setup lang="ts">
  import { useRemember } from "@inertiajs/vue3";

  const filters = useRemember<{
    search: string;
    status: "active" | "inactive" | "all";
  }>({
    search: "",
    status: "all",
  });
  </script>
  ```

  ```tsx React icon="react" theme={null}
  import { useRemember } from "@inertiajs/react";

  export default function Users() {
    const [filters, setFilters] = useRemember<{
      search: string;
      status: "active" | "inactive" | "all";
    }>({
      search: "",
      status: "all",
    });

    return null;
  }
  ```

  ```svelte Svelte icon="s" theme={null}
  <script lang="ts">
      import { useRemember } from '@inertiajs/svelte'

      const filters = useRemember<{
          search: string
          status: 'active' | 'inactive' | 'all'
      }>({
          search: '',
          status: 'all',
      })
  </script>
  ```
</CodeGroup>

## Khôi phục state

Phương thức `router.restore()` nhận generic để khai báo type state được khôi phục từ [history](/v3/data-props/remembering-state#manually-saving-state).

```ts theme={null}
import { router } from "@inertiajs/react";

interface TableState {
  sortBy: string;
  sortDesc: boolean;
  page: number;
}

const restored = router.restore<TableState>("table-state");

if (restored) {
  console.log(restored.sortBy);
}
```

## Router request

Các phương thức router nhận generic để khai báo kiểu cho request data, giúp type checking dữ liệu được gửi đi.

```ts theme={null}
import { router } from "@inertiajs/react";

interface CreateUserData {
  name: string;
  email: string;
}

router.post<CreateUserData>("/users", {
  name: "John",
  email: "john@example.com",
});
```

## Flash data theo scope

Phương thức `router.flash()` nhận generic để khai báo kiểu cho flash data riêng theo trang hoặc section, tách biệt với cấu hình `flashDataType` toàn cục.

```ts theme={null}
import { router } from "@inertiajs/react";

router.flash<{ paymentError: string }>({ paymentError: "Card declined" });
```

## Client-side visits

Các phương thức `router.push()` và `router.replace()` nhận generic để khai báo type props của [client-side visit](/v3/the-basics/manual-visits#client-side-visits).

```ts theme={null}
import { router } from "@inertiajs/react";

interface UserPageProps {
  user: { id: number; name: string };
}

router.push<UserPageProps>({
  component: "Users/Show",
  url: "/users/1",
  props: { user: { id: 1, name: "John" } },
});

router.replace<UserPageProps>({
  props: (current) => ({
    ...current,
    user: { ...current.user, name: "Updated" },
  }),
});
```

***

## Tài liệu chính thức

Bài dịch này được đối chiếu từ [tài liệu Inertia.js v3 chính thức](https://inertiajs.com/docs/v3/advanced/typescript). Nếu có khác biệt do phiên bản hoặc cập nhật mới, hãy ưu tiên tài liệu chính thức làm nguồn tham chiếu.
