inertia-rails-typescript

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Inertia Rails TypeScript Setup

Inertia Rails TypeScript 设置

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:
  • Shared props (auth, flash)? → Update
    SharedProps
    /
    FlashData
    in
    index.ts
    — InertiaConfig in
    globals.d.ts
    propagates them globally via
    usePage()
  • Page-specific props?
    type Props = { ... }
    in the page file only — never include shared props here
通过InertiaConfig模块扩展实现类型安全的共享props、flash和错误处理。该方案在React、Vue和Svelte中的使用方式完全一致——
globals.d.ts
InertiaConfig
的设置对所有框架都相同。
在添加TypeScript类型之前,请确认:
  • 是否有共享props(如auth、flash)? → 更新
    index.ts
    中的
    SharedProps
    /
    FlashData
    ——
    globals.d.ts
    中的InertiaConfig会通过
    usePage()
    将这些类型全局传播
  • 是否有页面专属props? → 仅在页面文件中定义
    type Props = { ... }
    ——不要在此处包含共享props

InertiaConfig Module Augmentation

InertiaConfig模块扩展

Define 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)
typescript
// 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
  }
}
typescript
// 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.
只需在全局定义一次共享props类型——不要在单个页面组件中重复定义。
InertiaConfig的属性名称必须完全一致——请勿重命名:
  • sharedPageProps
    (不能是sharedProps)
  • flashDataType
    (不能是flashProps或flashData)
  • errorValueType
    (不能是errorBag或errorType)
typescript
// 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
  }
}
typescript
// 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: ... }
作为共享props的键——这与Rails社区的
inertia_share
约定(
{ auth: { user: current_user } }
)一致。
auth
命名空间将认证数据与页面props分离,避免页面自身有
user
prop时发生冲突。请勿使用
current_user:
user:
作为顶层键——它们会与页面专属props冲突,且不符合其他Inertia技能和示例遵循的约定。

BAD vs GOOD Patterns

错误示例 vs 正确实践

tsx
// 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
:
typescript
// 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.
tsx
// 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。添加新的共享prop时,只需更新
index.ts
——不要修改
globals.d.ts
typescript
// 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'
会导致冲突。

Page-Specific Props

页面专属Props

Page components type ONLY their own props. Shared props (like auth) and flash come from InertiaConfig automatically.
页面组件仅需为自身的props定义类型。共享props(如auth)和flash会通过InertiaConfig自动获取类型。

type
vs
interface
for page props (React-specific)

页面props的
type
vs
interface
(仅React适用)

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.
PatternWorks with
usePage<T>()
?
Notes
type Props = { users: User[] }
YesPreferred — just works
interface Props { users: User[] }
No — TS2344Missing index signature
usePage<Required<Props>>()
YesWraps interface to add index signature
tsx
// 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} />
}
此限制仅针对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添加索引签名
tsx
// React
type Props = {
  users: User[]         // 仅页面专属props
  // auth不在这里——它通过InertiaConfig全局提供
}

export default function Index({ users }: Props) {
  // 单独访问共享props:
  const { props, flash } = usePage()
  // props.auth通过InertiaConfig获取类型
  // flash.notice通过InertiaConfig获取类型
  return <UserList users={users} />
}

Accessing shared props in Vue and Svelte

在Vue和Svelte中访问共享props

Vue and Svelte use different patterns to access shared props, but InertiaConfig typing works the same way.
vue
<!-- 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
<!-- 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>
Vue和Svelte使用不同的方式访问共享props,但InertiaConfig的类型定义方式保持一致。
vue
<!-- 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
<!-- Svelte — 从@inertiajs/svelte导入page store -->
<script lang="ts">
  import { page } from '@inertiajs/svelte'
  // $page.props.auth通过InertiaConfig获取类型
  // $page.flash.notice通过InertiaConfig获取类型
</script>

Common TypeScript Errors

常见TypeScript错误

ErrorCauseFix
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
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 untypedMissing InertiaConfigAdd
globals.d.ts
with module augmentation (see above)
InertiaConfig not taking effectDeclaration in wrong fileMust 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
错误原因修复方案
TS2344 错误在
usePage<Props>()
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
共享props无类型缺少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 Integration

Typelizer集成

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.
如果使用
typelizer
gem(参考
alba-inertia
技能),SharedProps会从序列化器自动生成——不要手动在
index.ts
中编写
SharedProps
接口。只需编写一次
globals.d.ts
(InertiaConfig扩展)。当你向
SharedPropsResource
添加新属性时,Typelizer会重新生成
index.ts
,类型会通过InertiaConfig自动传播——无需手动更新类型。

Related Skills

相关技能

  • Shared props setup
    inertia-rails-controllers
    (inertia_share)
  • Flash config
    inertia-rails-controllers
    (flash_keys)
  • Auto-generated types
    alba-inertia
    (Typelizer + Alba resources)
  • Page component props
    inertia-rails-pages
    (type Props pattern)
  • 共享props设置
    inertia-rails-controllers
    (inertia_share)
  • Flash配置
    inertia-rails-controllers
    (flash_keys)
  • 自动生成类型
    alba-inertia
    (Typelizer + Alba资源)
  • 页面组件props
    inertia-rails-pages
    (type Props写法)