tRPC v11 で型エラー・動かない時の対処法:v10からの移行で詰まる5つの破壊的変更【React Query v5 / createCallerFactory】

公開日:2026-07-22/カテゴリ:tRPC・TypeScript・React・Next.js/AnchorUp 技術ブログ

まず確認:tRPC v11 で動かない時の症状と対策(結論)

tRPC v11 は v10 との間に複数の破壊的変更がある。アップグレード後に型エラーや実行時エラーが発生した場合は、まず以下の表で症状を照合すること。

症状 原因 対策
createTRPCReact が型エラー / import できない パッケージ構成が変わった @trpc/react-query を v11 に合わせて更新
useQuery の型が合わない・ジェネリクスエラー React Query v5 が必須になった @tanstack/react-query を v5 に上げる
httpBatchLink から fetch 関連エラーが出る デフォルトリンクが httpBatchStreamLink になった httpBatchStreamLink に変更するか明示的に httpBatchLink を指定
サーバーサイドの createCaller が動かない createCallerFactory が新しい API になった appRouter.createCaller() から createCallerFactory に移行
superjson が Date を正しくシリアライズしない デフォルトトランスフォーマーが変わった transformer: superjson を明示的に設定

tRPC v11 で何が変わったのか

tRPC v11 は「React Query v5 への対応」と「ストリーミングのデフォルト化」が主な変更軸だ。

主な破壊的変更:

  1. React Query v5 が必須(v4 は非対応)
  2. httpBatchStreamLink がデフォルトのリンクに
  3. サーバーサイド呼び出しの API が createCallerFactory に刷新
  4. トランスフォーマー(superjson 等)の設定方法が変更
  5. inferRouterInputs / inferRouterOutputs の型ユーティリティ名が変更

v10 で動いていたコードがそのままでは動かなくなるケースが多く、エラーメッセージから原因を特定しにくいことが多い。以降で症状ごとに詳細を解説する。


症状1: React Query v5 が入っていないと型エラーになる

エラーメッセージ

Type error: Type 'UseQueryResult<...>' is not assignable to type '...'
Module '"@tanstack/react-query"' has no exported member 'QueryOptions'

原因

tRPC v11 の @trpc/react-query は React Query v5 専用に設計されている。v4 では型が合わず、実行時エラーにもなる。

対処法

まず React Query を v5 にアップグレードする。

npm install @tanstack/react-query@5 @trpc/react-query@11 @trpc/client@11 @trpc/server@11

React Query v5 自体にも破壊的変更がある(useQuery のオプション名変更など)。tRPC の移行と同時に発生するため混乱しやすいが、分けて対処すること。

React Query v5 の主な変更点:

// v4 まで
const { data } = useQuery(['key'], fetcher, { onSuccess: (data) => {} })

// v5 から(コールバックが削除・options の形式が変更)
const { data } = useQuery({ queryKey: ['key'], queryFn: fetcher })
// onSuccess は useEffect で代替する

症状2: httpBatchStreamLink への移行

エラーメッセージ

Error: unable to transform response
RangeError: Invalid typed array length

原因

tRPC v11 では httpBatchStreamLink がデフォルト推奨リンクになった。v10 では httpBatchLink がデフォルトだったが、v11 では ストリーミングレスポンスを使う httpBatchStreamLink への移行が推奨されている。

サーバーサイドがストリーミングに対応していない場合(一部のエッジランタイムや Serverless 環境)、httpBatchStreamLink を使うとエラーになることがある。

対処法

ストリーミング対応環境(Node.js, Cloudflare Workers 等):

// trpc/client.ts
import { createTRPCClient, httpBatchStreamLink } from '@trpc/client'
import type { AppRouter } from '../server/routers/_app'

export const trpc = createTRPCClient<AppRouter>({
  links: [
    httpBatchStreamLink({
      url: '/api/trpc',
      headers() {
        return {
          authorization: getAuthToken(),
        }
      },
    }),
  ],
})

ストリーミング非対応環境(Vercel Serverless Functions の一部構成等):

// httpBatchLink を明示的に使う
import { createTRPCClient, httpBatchLink } from '@trpc/client'

export const trpc = createTRPCClient<AppRouter>({
  links: [
    httpBatchLink({
      url: '/api/trpc',
    }),
  ],
})

症状3: createCallerFactory でサーバーサイド呼び出しが動かない

エラーメッセージ

TypeError: appRouter.createCaller is not a function
Property 'createCaller' does not exist on type 'Router<...>'

原因

v10 では appRouter.createCaller(ctx) でサーバーサイド呼び出しができたが、v11 では createCallerFactory という新しい API に変わった。

対処法

v10 の書き方(非推奨):

// v10 まで
const caller = appRouter.createCaller({ user: session.user })
const result = await caller.user.getProfile()

v11 の書き方:

// server/trpc.ts
import { initTRPC } from '@trpc/server'
import type { Context } from './context'

const t = initTRPC.context<Context>().create()

export const router = t.router
export const publicProcedure = t.procedure
export const createCallerFactory = t.createCallerFactory  // ← エクスポート
// server/routers/_app.ts
import { router } from '../trpc'
import { userRouter } from './user'

export const appRouter = router({
  user: userRouter,
})

export type AppRouter = typeof appRouter
// Next.js の Server Component やサーバーサイド処理で使う
import { createCallerFactory } from '../server/trpc'
import { appRouter } from '../server/routers/_app'
import { createContext } from '../server/context'

// factory を作成(モジュールレベルで1回だけ)
const createCaller = createCallerFactory(appRouter)

// リクエストごとにコンテキストを渡してインスタンス化
const caller = createCaller(await createContext())
const profile = await caller.user.getProfile()

createCallerFactory を使うことで、コンテキストとルーターを分離して管理できるようになった。テストでモックコンテキストを差し込みやすくなるメリットもある。


症状4: superjson で Date が文字列になる・シリアライズが壊れる

症状

// サーバー側で Date を返しているのに、クライアントで string になる
const user = await trpc.user.getById.query(1)
console.log(user.createdAt)          // "2024-01-01T00:00:00.000Z" (string)
console.log(user.createdAt instanceof Date)  // false

原因

tRPC v11 ではデフォルトのデータシリアライザーが変更され、JSON のみになった。v10 まで superjson を明示的に設定していた場合、設定方法が変わっている。

対処法

npm install superjson
// server/trpc.ts
import { initTRPC } from '@trpc/server'
import superjson from 'superjson'
import type { Context } from './context'

const t = initTRPC.context<Context>().create({
  transformer: superjson,  // ← 明示的に指定する
})

export const router = t.router
export const publicProcedure = t.procedure
export const createCallerFactory = t.createCallerFactory
// trpc/client.ts(クライアント側にも同じトランスフォーマーを設定する)
import { createTRPCClient, httpBatchStreamLink } from '@trpc/client'
import superjson from 'superjson'
import type { AppRouter } from '../server/routers/_app'

export const trpc = createTRPCClient<AppRouter>({
  links: [
    httpBatchStreamLink({
      url: '/api/trpc',
      transformer: superjson,  // ← クライアント側にも設定
    }),
  ],
})

サーバーとクライアントで 必ず同じトランスフォーマーを設定すること。片方だけ設定するとシリアライズ/デシリアライズのミスマッチが起きる。


症状5: inferRouterInputs / inferRouterOutputs の型が取れない

エラーメッセージ

Module '"@trpc/server"' has no exported member 'inferRouterInputs'

原因

v10 では @trpc/server から直接 inferRouterInputs / inferRouterOutputs をインポートできたが、v11 では型ユーティリティのインポートパスが整理された。

対処法

// v10 まで
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'

// v11 から(同じパスで引き続き使える場合もあるが、ルーターから推論する方が安全)
import type { AppRouter } from './server/routers/_app'
import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'

type RouterInputs = inferRouterInputs<AppRouter>
type RouterOutputs = inferRouterOutputs<AppRouter>

// 使用例
type UserProfileInput = RouterInputs['user']['getProfile']
type UserProfileOutput = RouterOutputs['user']['getProfile']

インポートエラーが出る場合は tRPC 全パッケージのバージョンが揃っているか確認すること。

# バージョン確認
npm list @trpc/server @trpc/client @trpc/react-query @trpc/next

混在するとピア依存関係の不整合で型エラーが起きる。


Next.js App Router との組み合わせ

Next.js App Router で tRPC v11 を使う場合、createTRPCProxyClient ではなく createTRPCClient を使う(v11 では proxy 版は廃止)。

// app/trpc/server.ts(Server Components 用)
import { createCallerFactory } from '../../server/trpc'
import { appRouter } from '../../server/routers/_app'
import { createContext } from '../../server/context'
import { cache } from 'react'

// React の cache() でリクエストごとにメモ化
export const createCaller = cache(async () => {
  const context = await createContext()
  const factory = createCallerFactory(appRouter)
  return factory(context)
})
// app/page.tsx(Server Component)
import { createCaller } from './trpc/server'

export default async function Page() {
  const caller = await createCaller()
  const users = await caller.user.list()  // 型安全なサーバーサイド呼び出し

  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}

v10 から v11 への移行チェックリスト

□ @trpc/server, @trpc/client, @trpc/react-query をすべて v11 に更新した
□ @tanstack/react-query を v5 に更新した
□ appRouter.createCaller() を createCallerFactory に移行した
□ httpBatchLink を httpBatchStreamLink に変更(または環境に合わせて明示指定)した
□ superjson を使う場合、サーバーとクライアント両方に transformer を設定した
□ React Query v5 の破壊的変更(onSuccess 廃止等)にも対応した

まとめ

tRPC v11 で動かなくなる主な原因は React Query v5 への強制移行 と 新しいサーバーサイド呼び出し API(createCallerFactory)、そして デフォルトリンクの変更(httpBatchStreamLink) の3点だ。

パッケージのバージョンが混在しているとエラー原因の特定が難しくなるため、npm list @trpc/* でバージョンを揃えてから対処することを強くすすめる。

技術ブログの記事一覧へ