inertia-rails-typescript by inertia-rails/skills
npx skills add https://github.com/inertia-rails/skills --skill inertia-rails-typescript使用 InertiaConfig 模块增强实现类型安全的共享属性、闪存数据和错误信息。在 React、Vue 和 Svelte 中工作方式完全相同 —— globals.d.ts 和 InertiaConfig 的配置对所有框架都是一样的。
在添加 TypeScript 类型之前,请先确认:
index.ts 中的 SharedProps/FlashData —— globals.d.ts 中的 InertiaConfig 会通过 usePage() 将它们全局传播type Props = { ... } —— 这里永远不要包含共享属性广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
全局定义共享属性类型仅需一次 —— 永远不要在单个页面组件中定义。
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 // 精确名称 —— 为 usePage().props 自动提供类型 flashDataType: FlashData // 精确名称 —— 为 usePage().flash 自动提供类型 errorValueType: string[] // 精确名称 —— 错误信息是字符串数组 } }
// 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 技能和示例所遵循的约定。
// 错误 —— 将共享属性作为泛型传递:
// usePage<{ users: User[], auth: AuthData, flash: FlashData }>()
// 错误 —— 将 SharedProps 接口扩展到页面属性中:
// interface Props extends SharedData { users: User[] }
// 错误 —— 声明 PageProps 接口:
// interface PageProps { auth: AuthData; flash: FlashData }
// 错误 —— 使用 current_user 或 user 作为顶级共享键:
// interface SharedProps { current_user: User }
// 错误 —— 直接从 usePage() 解构 auth(TS2339: 'auth' 在 Page 上不存在):
// const { auth } = usePage()
// usePage() 返回一个包含 { props, flash, component, url, ... } 的 Page 对象
// auth 存在于 props 内部,而不是 Page 对象本身上
// 错误 —— 在 index.ts 中重复 InertiaConfig(它应该属于 globals.d.ts):
// declare module '@inertiajs/core' { ... } ← 在 index.ts 中
// 正确 —— 从 usePage().props 获取 props,从 usePage().flash 获取 flash:
const { props, flash } = usePage()
// props.auth 已获得类型(通过 InertiaConfig 从 SharedProps 获得)
// flash.notice 已获得类型(通过 InertiaConfig 从 FlashData 获得)
重要: globals.d.ts 配置 InertiaConfig 仅需一次。当添加新的共享属性时,只更新 index.ts —— 不要修改 globals.d.ts:
// 之前 —— app/frontend/types/index.ts
export interface SharedProps {
auth: { user?: { id: number; name: string; email: string } }
}
// 之后 —— 在此处添加新键,而不是在 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)和闪存数据通过 InertiaConfig 自动获得。
type 与 interface(特定于 React)此约束仅适用于 React。Vue 的 defineProps<T>() 和 Svelte 的 $props() 不使用 usePage<T>() 泛型,因此在那里使用 interface 没有问题。
usePage<T>() 要求 T 具有索引签名。type 别名隐式具有索引签名;interface 声明则没有。将 interface 与 usePage 一起使用会导致编译时出现 TS2344 错误。
| 模式 | 是否适用于 usePage<T>()? | 备注 |
|---|---|---|
type Props = { users: User[] } | 是 | 首选 —— 直接可用 |
interface Props { users: User[] } | 否 —— TS2344 | 缺少索引签名 |
usePage<Required<Props>>() | 是 | 包装 interface 以添加索引签名 |
// React
type Props = {
users: User[] // 仅页面特定
// auth 不在这里 —— 它通过 InertiaConfig 全局提供
}
export default function Index({ users }: Props) {
// 单独访问共享属性:
const { props, flash } = usePage()
// props.auth 通过 InertiaConfig 获得类型
// flash.notice 通过 InertiaConfig 获得类型
return <UserList users={users} />
}
Vue 和 Svelte 使用不同的模式来访问共享属性,但 InertiaConfig 的类型化工作方式相同。
<!-- Vue 3 —— usePage() 返回响应式对象;使用 computed() 获取派生值 -->
<script setup lang="ts">
import { usePage } from '@inertiajs/vue3'
import { computed } from 'vue'
const page = usePage()
const userName = computed(() => page.props.auth.user?.name) // 通过 InertiaConfig 获得类型
</script>
<!-- Svelte —— 来自 @inertiajs/svelte 的 page store -->
<script lang="ts">
import { page } from '@inertiajs/svelte'
// $page.props.auth 通过 InertiaConfig 获得类型
// $page.flash.notice 通过 InertiaConfig 获得类型
</script>
| 错误 | 原因 | 修复方法 |
|---|---|---|
usePage<Props>() 上的 TS2344 | interface 缺少索引签名 | 使用 type Props = { ... } 代替 interface,或者包装:usePage<Required<Props>>() |
TS2339 'auth' 在 Page 类型上不存在 | 直接从 usePage() 解构 auth | usePage() 返回 { props, flash, ... } —— 使用 usePage().props.auth,而不是 usePage().auth |
TS2339 'flash' 在类型上不存在 | 访问 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 数组中是否包含 types 目录 |
如果使用 typelizer gem(参见 alba-inertia 技能),SharedProps 会自动从您的序列化器生成 —— 不要在 index.ts 中手动编写 SharedProps 接口。您只需编写一次 globals.d.ts(InertiaConfig 增强)。当您向 SharedPropsResource 添加新属性时,Typelizer 会重新生成 index.ts,并且类型会通过 InertiaConfig 传播 —— 无需手动更新类型。
inertia-rails-controllers (inertia_share)inertia-rails-controllers (flash_keys)alba-inertia (Typelizer + Alba resources)inertia-rails-pages (type Props 模式)每周安装量
70
代码仓库
GitHub 星标数
35
首次出现
2026年2月13日
安全审计
已安装于
gemini-cli69
opencode69
codex69
github-copilot68
amp67
kimi-cli66
Type-safe shared props, flash, and errors using InertiaConfig module augmentation. Works identically across React, Vue, and Svelte — the globals.d.ts and InertiaConfig setup is the same for all frameworks.
Before adding TypeScript types, ask:
SharedProps/FlashData in index.ts — InertiaConfig in globals.d.ts propagates them globally via usePage()type Props = { ... } in the page file only — never include shared props hereDefine shared props type ONCE globally — never in individual page components.
InertiaConfig property names are EXACT — do not rename them:
sharedPageProps (NOT sharedProps)
flashDataType (NOT flashProps, NOT flashData)
errorValueType (NOT errorBag, NOT 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 } } }
Convention: Use auth: { user: ... } as the shared props key — this matches the Rails inertia_share community convention ({ auth: { user: current_user } }). The auth namespace separates authentication data from page props, preventing collisions when a page has its own user prop. Do NOT use current_user: or user: as top-level keys — they collide with page-specific props and break the convention that other Inertia skills and examples assume.
// 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)
Important: globals.d.ts configures InertiaConfig ONCE. When adding a new shared prop, only update index.ts — do NOT touch 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 }
}
InertiaConfig in globals.d.ts references SharedProps by name — it picks up the change automatically. Adding a second declare module '@inertiajs/core' causes conflicts.
Page components type ONLY their own props. Shared props (like auth) and flash come from InertiaConfig automatically.
type vs interface for page props (React-specific)This constraint applies to React only. Vue's defineProps<T>() and Svelte's $props() do not use usePage<T>() generics, so interface works fine there.
usePage<T>() requires T to have an index signature. type aliases have one implicitly; interface declarations do not. Using interface with usePage causes TS2344 at compile time.
| Pattern | Works with usePage<T>()? | Notes |
|---|---|---|
type Props = { users: User[] } | Yes | Preferred — just works |
interface Props { users: User[] } | No — TS2344 | Missing index signature |
usePage<Required<Props>>() | Yes | Wraps interface to add index signature |
// 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 and Svelte use different patterns to access shared props, but InertiaConfig typing works the same way.
<!-- Vue 3 — usePage() returns reactive object; use computed() for derived values -->
<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>
<!-- Svelte — page store from @inertiajs/svelte -->
<script lang="ts">
import { page } from '@inertiajs/svelte'
// $page.props.auth is typed via InertiaConfig
// $page.flash.notice is typed via InertiaConfig
</script>
| Error | Cause | Fix |
|---|---|---|
TS2344 on usePage<Props>() | interface lacks index signature | Use type Props = { ... } instead of interface, or wrap: usePage<Required<Props>>() |
TS2339 'auth' does not exist on type Page | Destructuring auth from usePage() directly |
If using the typelizer gem (see alba-inertia skill), SharedProps are auto-generated from your serializer — do NOT manually write the SharedProps interface in index.ts. You only write globals.d.ts once (the InertiaConfig augmentation). When you add a new attribute to SharedPropsResource, Typelizer regenerates index.ts and the types propagate via InertiaConfig — no manual type updates needed.
inertia-rails-controllers (inertia_share)inertia-rails-controllers (flash_keys)alba-inertia (Typelizer + Alba resources)inertia-rails-pages (type Props pattern)Weekly Installs
70
Repository
GitHub Stars
35
First Seen
Feb 13, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli69
opencode69
codex69
github-copilot68
amp67
kimi-cli66
World Monitor 智能仪表板:AI驱动的全球实时监控与地缘风险分析平台
677 周安装
TorchTitan:PyTorch原生分布式大语言模型预训练平台,支持4D并行与H100 GPU加速
69 周安装
screenshot 截图技能:跨平台桌面截图工具,支持macOS/Linux权限管理与多模式捕获
69 周安装
tmux进程管理最佳实践:交互式Shell初始化、会话命名与生命周期管理
69 周安装
Git Rebase Sync:安全同步分支的Git变基工具,解决冲突与备份
69 周安装
LinkedIn自动化工具 - Claude Code专属,自然对话拓展人脉,避免垃圾信息
69 周安装
实验流水线框架:4阶段科研实验执行与消融研究方法论 | EvoScientist
69 周安装
usePage() returns { props, flash, ... } — use usePage().props.auth, not usePage().auth |
TS2339 'flash' does not exist on type | Accessing usePage().props.flash | Flash is top-level: usePage().flash, NOT usePage().props.flash |
| Shared props untyped | Missing InertiaConfig | Add globals.d.ts with module augmentation (see above) |
| InertiaConfig not taking effect | Declaration in wrong file | Must be in a .d.ts file (e.g., globals.d.ts), not in .ts — TypeScript ignores declare module in regular .ts files that have imports/exports |
| Types correct but IDE shows errors | globals.d.ts not included | Verify tsconfig.app.json includes the types directory in include array |