关于 inertia-rails-typescript
面向 React、Vue 或 Svelte Inertia.js 项目的 TypeScript 配置指南,说明如何为共享属性、flash 数据和错误设置全局类型,并介绍页面专属属性的类型模式。
主要功能
- 01扩展 InertiaConfig 模块
- 02全局共享属性类型
- 03Flash 与错误类型定义
- 04页面专属属性模式
- 05Typelizer 集成指导
适用场景
- 01为共享身份验证属性添加类型
- 02配置 flash 和错误类型
- 03解决 usePage TypeScript 错误
- 04使用 Typelizer 生成共享属性类型
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ inertia-rails/ skills --skill inertia-rails-typescript安装后在 Claude Code 中输入/inertia-rails-typescript确认已加载。
详细介绍
Inertia Rails TypeScript 设置
使用 InertiaConfig 模块扩展,为共享属性、闪存消息和错误提供类型安全支持。
在 React、Vue 和 Svelte 中的工作方式完全相同——所有框架都使用相同的 globals.d.ts 和 InertiaConfig 设置。
添加 TypeScript 类型前,请先确认:
- 共享属性(auth、flash)? → 在
index.ts中更新SharedProps/FlashData——globals.d.ts中的 InertiaConfig 会通过usePage()将其全局传播 - 页面专属属性? → 仅在页面文件中使用
type Props = { ... }——切勿在此处包含共享属性
InertiaConfig 模块扩展
仅在全局定义一次共享属性类型——切勿在单个页面组件中定义。
InertiaConfig 属性名称必须完全准确——不要重命名:
sharedPageProps(不是 sharedProps)flashDataType(不是 flashProps,也不是 flashData)errorValueType(不是 errorBag,也不是 errorType)
// app/frontend/types/globals.d.ts
import type { FlashData, SharedProps } from '@/types'
declare module '@inertiajs/core' {
export interface InertiaConfig {
sharedPageProps: SharedProps // EXACT name — auto-typed for usePage().props
flashDataType: FlashData // EXACT name — auto-typed for usePage().flash
errorValueType: string[] // EXACT name — errors are arrays of strings
}
}
// app/frontend/types/index.ts
export interface FlashData {
notice?: string
alert?: string
}
export interface SharedProps {
auth: { user?: { id: number; name: string; email: string } }
}
约定: 使用 auth: { user: ... } 作为共享属性键——这与 Rails 的 inertia_share 社区约定({ auth: { user: current_user } })一致。
auth 命名空间可将身份验证数据与页面属性分开,避免页面自身具有 user 属性时发生冲突。不要将 current_user: 或 user: 用作顶层键——它们会与页面专属属性冲突,并破坏其他 Inertia 技能和示例所遵循的约定。
错误与正确的模式
// BAD — passing shared props as generics:
// usePage<{ users: User[], auth: AuthData, flash: FlashData }>()
// BAD — extending a SharedProps interface into page props:
// interface Props extends SharedData { users: User[] }
// BAD — declaring PageProps interface:
// interface PageProps { auth: AuthData; flash: FlashData }
// BAD — using current_user or user as top-level shared key:
// interface SharedProps { current_user: User }
// BAD — destructuring auth directly from usePage() (TS2339: 'auth' does not exist on Page):
// const { auth } = usePage()
// usePage() returns a Page object with { props, flash, component, url, ... }
// auth lives inside props, not on the Page itself
// BAD — duplicating InertiaConfig in index.ts (it belongs in globals.d.ts):
// declare module '@inertiajs/core' { ... } ← in index.ts
// GOOD — props from usePage().props, flash from usePage().flash:
const { props, flash } = usePage()
// props.auth is typed (from SharedProps via InertiaConfig)
// flash.notice is typed (from FlashData via InertiaConfig)
重要: globals.d.ts 只配置一次 InertiaConfig。添加新的共享属性时,只更新 index.ts——不要修改 globals.d.ts:
// BEFORE — app/frontend/types/index.ts
export interface SharedProps {
auth: { user?: { id: number; name: string; email: string } }
}
// AFTER — add the new key here, NOT in globals.d.ts
export interface SharedProps {
auth: { user?: { id: number; name: string; email: string } }
notifications: { unread_count: number }
}
globals.d.ts 中的 InertiaConfig 按名称引用 SharedProps——它会自动获取更改。再添加一个 declare module '@inertiajs/core' 会导致冲突。
页面专属属性
页面组件只为自身的属性指定类型。共享属性(例如 auth)和 flash 会自动从 InertiaConfig 获取。
页面属性使用 type 还是 interface(仅适用于 React)
此限制仅适用于 React。Vue 的 defineProps<T>() 和 Svelte 的
$props() 不使用 usePage<T>() 泛型,因此在这些框架中使用 interface 没问题。
usePage<T>() 要求 T 具有索引签名。type 别名会隐式具有索引签名;interface 声明则不会。在 usePage 中使用 interface 会在编译时导致 TS2344。
| 模式 | 能否与 usePage<T>() 搭配使用? | 说明 |
|---|---|---|
type Props = { users: User[] } | 可以 | 首选——直接可用 |
interface Props { users: User[] } | 不可以 — TS2344 | 缺少索引签名 |
usePage<Required<Props>>() | 可以 | 包装 interface,以添加索引签名 |
// React
type Props = {
users: User[] // page-specific only
// auth is NOT here — it comes from InertiaConfig globally
}
export default function Index({ users }: Props) {
// Access shared props separately:
const { props, flash } = usePage()
// props.auth is typed via InertiaConfig
// flash.notice is typed via InertiaConfig
return <UserList users={users} />
}
在 Vue 和 Svelte 中访问共享属性
Vue 和 Svelte 使用不同的模式来访问共享属性,但 InertiaConfig 的类型设置方式相同。
<script setup lang="ts">
import { usePage } from '@inertiajs/vue3'
import { computed } from 'vue'
const page = usePage()
const userName = computed(() => page.props.auth.user?.name) // typed via InertiaConfig
</script>
<script lang="ts">
import { page } from '@inertiajs/svelte'
// $page.props.auth is typed via InertiaConfig
// $page.flash.notice is typed via InertiaConfig
</script>
常见 TypeScript 错误
| 错误 | 原因 | 修复方法 |
|---|---|---|
usePage<Props>() 上的 TS2344 | interface 缺少索引签名 | 使用 type Props = { ... } 替代 interface,或进行包装:usePage<Required<Props>>() |
TS2339 'auth' does not exist on type Page | 直接从 usePage() 解构 auth | usePage() 返回 { props, flash, ... }——使用 usePage().props.auth,而不是 usePage().auth |
TS2339 'flash' does not exist on type | 访问 usePage().props.flash | Flash 位于顶层:usePage().flash,而不是 usePage().props.flash |
| 共享属性没有类型 | 缺少 InertiaConfig | 添加包含模块扩展的 globals.d.ts(见上文) |
| InertiaConfig 未生效 | 声明位于错误的文件中 | 必须放在 .d.ts 文件中(例如 globals.d.ts),而不是 .ts 文件中——TypeScript 会忽略包含导入/导出的常规 .ts 文件中的 declare module |
| 类型正确,但 IDE 显示错误 | 未包含 globals.d.ts | 确认 tsconfig.app.json 的 include 数组包含类型目录 |
Typelizer 集成
如果使用 typelizer gem(参见 alba-inertia 技能),SharedProps 会根据序列化器自动生成——不要在 index.ts 中手动编写 SharedProps 接口。你只需编写一次 globals.d.ts(即 InertiaConfig 扩展)。向 SharedPropsResource 添加新属性时,Typelizer 会重新生成 index.ts,类型也会通过 InertiaConfig 自动传递,无需手动更新类型。
相关技能
- Shared props 设置 →
inertia-rails-controllers(inertia_share) - Flash 配置 →
inertia-rails-controllers(flash_keys) - 自动生成类型 →
alba-inertia(Typelizer + Alba 资源) - 页面组件 props →
inertia-rails-pages(type Props 模式)