Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Typescript型推論の限界
Search
nikawa2161
August 06, 2025
73
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Typescript型推論の限界
nikawa2161
August 06, 2025
More Decks by nikawa2161
See All by nikawa2161
Node.js Subpath imports
nikawa2161
0
21
Sagaパターン入門(続編)
nikawa2161
0
32
Sagaパターン入門
nikawa2161
0
41
沖縄観光とPostgreSQL排他制約の話
nikawa2161
0
35
20251209_プログラミング原則の学び
nikawa2161
0
25
自分のコードを数年ぶりに読んだら
nikawa2161
0
20
ユーザーインタビュー分析に参加して得られたことと気づき
nikawa2161
0
25
oEmbedとは?
nikawa2161
0
100
はじめまして、にかわです
nikawa2161
0
20
Featured
See All Featured
実際に使うSQLの書き方 徹底解説 / pgcon21j-tutorial
soudai
PRO
201
75k
For a Future-Friendly Web
brad_frost
183
10k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
66
55k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Rails Girls Zürich Keynote
gr2m
96
14k
A designer walks into a library…
pauljervisheath
211
24k
Paper Plane
katiecoart
PRO
1
52k
A better future with KSS
kneath
240
18k
GitHub's CSS Performance
jonrohan
1033
470k
A Guide to Academic Writing Using Generative AI - A Workshop
ks91
PRO
1
330
Deep Space Network (abreviated)
tonyrice
0
210
How GitHub (no longer) Works
holman
316
150k
Transcript
TypeScript の型推論の限界 Hono でのエラーハンドリングとクライアント型安全性の挑戦 1
目標 クライアント側でステータス毎にエラーの型を推論し、型安全にハンドリングしたい 現状: hc<AppType>() で成功時は型推論できるが onError で型情報が失われる // 理想:ステータスコードに応じた型推論 if
(res.status === 400) { // クライアント側でエラーの型が推論できている console.log(res.data.error); } 2
背景:型推論が失われる瞬間 // サーバー側 const app = new Hono() .get("/users", (c)
=> c.json({ users: [] })) .onError((err, c) => { return c.json({ error: err.message }, 500); // この型が hc 側へ伝播しない }); export type AppType = typeof app; // クライアント側 const client = hc<AppType>("/api"); const res = await client.users.$get(); // エラーのステータスが分からない 3
試行錯誤の軌跡 アプローチ 1: onError での例外キャッチ 狙い: カスタムクラスを throw し、onError で一括キャッチ
一括で管理できるかつ、シンプルなのでこちらでできるならベスト 例外 → ステータス変換 app.onError((err, c) => { if (err instanceof BadRequestError) { return c.json({ error: err.message }, 400); } return c.json({ error: "Internal error" }, 500); }); 4
アプローチ 1 の失敗理由 失敗理由: onError の返却型が hc に届かない middleware のレスポンスの型がサポートされていない?
(GitHub Issue #2719) compose() で例外時に onError に投げる仕様のため、推論できない 5
アプローチ 2: カスタムエラークラス 3 パターン パターン 1: 親クラス + ステータス込み
class CustomError extends Error { constructor(message: string, public status: StatusCode = 500) { super(message); } } export class BadRequestError extends CustomError { constructor(message: string, public status: 400 = 400) { super(message); } } 6
パターン 2: 型マッチでステータス指定 export class BadRequestError extends Error { constructor(message:
string, public status: 400 = 400) { super(message); } } export function handleError(c: Context, err: unknown) { if (err instanceof BadRequestError) { return c.json({ error: err.message }, err.status); } } 7
パターン 3: 純粋クラス + パターンマッチ export class BadRequestError extends Error
{ constructor(message: string) { super(message); } } export function handleError(c: Context, err: unknown) { if (err instanceof BadRequestError) { return c.json({ error: err.message }, 400); // リテラル指定 } } 結果: 同じ理由で失敗 8
なぜ全て失敗したのか? Hono の型推論の仕様制約 1. ハンドラ内直接記述が必須 // 推論される app.get("/users", (c) =>
{ return c.json({ error: "Bad Request" }, 400); }); // 推論されない app.get("/users", (c) => { return handleError(c, error); // 関数経由 }); 2. モジュールスコープの制約 ハンドラー外で定義された関数の戻り値は推論されない 9
全アプローチ共通の失敗理由 ルートハンドラ内で直接記載されていない: Hono はハンドラ内で直接記述された c.json<Body, Status> のみ推論する その Status がリテラル(
400 , 404 ...)である必要 handleError がモジュール外にある: 外部関数経由では推論が働かない 結果: const res = await client.$get(); ではエラーのステータスは分からない 10
主なボトルネック 1. onError の型推論限界 これは throw された場合にハンドラー外で実行されるため hono の仕様外 app.onError((err,
c) => { return c.json({ error: "Something went wrong" }, 500); // ← この型が hc 側へ伝播しない }); 11
2. モジュールスコープの制約 ハンドラ内に直接記述しないと型推論ができない仕様を突破できない // 外部関数では型推論されない export function handleError(c: Context, err:
unknown) { if (err instanceof BadRequestError) { return c.json({ error: err.message }, 400); // この型が伝播しない } } // ハンドラ内直接記述のみ推論される app.get("/users", (c) => { return c.json({ error: "Not found" }, 404); // この型は推論される }); 12
現在の落とし所 カスタムエラーで投げたものを onError 内でパターンマッチして一括管理 app.onError((err, c) => { if (err
instanceof BadRequestError) { return c.json({ error: err.message }, 400); } if (err instanceof NotFoundError) { return c.json({ error: err.message }, 404); } if (err instanceof UnauthorizedError) { return c.json({ error: err.message }, 401); } // デフォルトエラー return c.json({ error: "Internal Server Error" }, 500); }); 13
達成できたこと vs 残る課題 達成できたこと: エラーハンドリングの共通化 課題だった型の伝播は達成できず: const res = await
client.$get() でエラーのステータスは型推論されない クライアント側でのステータス推論は別途検討が必要 14
今後の検討事項 個別にステータスを定義するか ルート側から明示的にエラーステータスを定義して型として共有する方法を考える 15
コンパイル時間への配慮 大規模アプリケーションでは型推論コストが課題 16
学びと知見 教訓 型安全にするために色々検証したが、仕様を突破するのが難しかった TypeScript vs Hono の問題の切り分けが曖昧だったので、知識不足を痛感 型安全にするために段階的に改善していく必要がある 17
TypeScript × フレームワークの制約 実行経路が分岐すると型推論が破綻 throw → catch → onError の流れで型情報が失われる
フレームワーク側が例外ルートでの型推論をサポートしていない 18
設計上の判断 完璧な型安全性より実用性を優先 フレームワークの制約を理解して設計 段階的改善でリスクを最小化 19
まとめ TypeScript の型安全性を追求する過程で、フレームワークの制約と現実的な解決策のバ ランス。 完璧な型安全性 < 保守可能で実用的な解決策 20
参考資料 Hono RPC Guide Elegant Error Handling with Hono RPC
(2024) GitHub Issue #2719 - middleware 型サポート GitHub Issue #3485 - RPC client 型推論問題 21