0Pricing
TypeScript Academy · レッスン

型レベルのミニルートパーサー

ルートパスを型付きのパラメーターオブジェクトに解析します。

「型レベルのミニルートパーサー」はCoddyKit上の無料TypeScript Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはTypeScript Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 TypeScript Academyコースには全4レッスンが含まれています。

目標

ルートパーサーを構築します。このパーサーは"users/:id/posts/:postId"のようなパスを読み取り、完全にコンパイル時に、型付きの params オブジェクト{ id: string; postId: string }を生成します。これは型付きルーターで実際に使われているパターンです。

type Params = ParseRoute<"users/:id/posts/:postId">;
// Goal: { id: string; postId: string }

パラメーターを認識する

パスのセグメントは、":"で始まる場合にパラメーターです。テンプレートリテラルでこのプレフィックスに一致させ、コロンの後にあるパラメーター名を推論します。

表記: 実際の TypeScript では、テンプレートリテラル型を、ドル記号と波括弧の穴を含むバッククォートで囲まれた文字列として記述します。ここでのスニペットでは、そのパターンをTpl<...>として表し、各部分を順番に並べています。たとえば、リテラルのプレフィックスに続く Rest に一致するバッククォートテンプレートは、Tpl<'prefix', infer Rest>と表します。

type ParamName<S> = S extends Tpl<':', infer Name> ? Name : never;
// Tpl<...> denotes a backtick template literal type: a colon then Name

type X = ParamName<':id'>;   // 'id'
type Y = ParamName<'users'>; // never

パスを分割する

まず、Split型を再利用して、ルートを"/"でセグメントに分割します。各セグメントは、リテラルまたはパラメーターのいずれかになります。

type Split<S extends string, Sep extends string> =
  S extends Tpl<infer H, Sep, infer T> ? [H, ...Split<T, Sep>] : [S];
// Tpl<H, Sep, T> = a template literal type matching H, then Sep, then T

type Segs = Split<'users/:id', '/'>; // ['users', ':id']

テンプレートの直接再帰

先に分割せず、セグメントを1つずつ一致させて元の文字列を直接解析することもできます。次の"/"までを一致させ、そのセグメントを処理してから、残りの部分に対して再帰します。

type ParseRoute<S extends string> =
  S extends Tpl<infer Seg, '/', infer Rest>
    ? SegParam<Seg> & ParseRoute<Rest>
    : SegParam<S>;
// Tpl<Seg, '/', Rest> matches a segment, a slash, then the rest

セグメントごとのパラメーター

ヘルパーは、1つのセグメントを、パラメーターであれば1プロパティのオブジェクトに、リテラルであれば空のオブジェクトに変換します。

type SegParam<S extends string> =
  S extends Tpl<':', infer Name> ? { [K in Name]: string } : {};
// Tpl<':', Name> matches a colon followed by the param name

type X = SegParam<':id'>;   // { id: string }
type Y = SegParam<'posts'>; // {}

交差型で組み合わせる

各セグメントが持つ params を交差型で組み合わせます。交差型の中では空のオブジェクトが消えるため、最終的な型には実際のパラメーターだけが残ります。

type Params = ParseRoute<"users/:id/posts/:postId">;
// { id: string } & {} & { postId: string } & {}
// = { id: string; postId: string }

解析をたどる

"users/:id/posts/:postId"の場合:

  • Seg "users" -> {} 、":id/posts/:postId" に対して再帰
  • Seg ":id" -> { id: string }、"posts/:postId" に対して再帰
  • Seg "posts" -> {}、":postId" に対して再帰
  • Seg ":postId" -> { postId: string }、基本ケース
type Final = { id: string } & { postId: string };

結果を整える

オブジェクトの交差型は、そのままでは表示が扱いにくくなります。「prettify」ヘルパーは、キーに対する mapped type を使って交差型を1つの見やすいオブジェクト型に平坦化します。

type Prettify<T> = { [K in keyof T]: T[K] } & {};

type Clean = Prettify<ParseRoute<"users/:id">>; // { id: string }

パーサーを使用する

解析した型を関数に組み込み、ハンドラーが正しく型付けされた params を受け取れるようにします。存在しないキーを渡すとコンパイルエラーになります。

declare function route<P extends string>(
  path: P,
  handler: (params: Prettify<ParseRoute<P>>) => void
): void;

route("users/:id", p => { const id: string = p.id; });

型付き params の利点

コンパイラーは、ルート文字列そのものからparamsを導出できるようになりました。パス内のパラメーター名を変更すると、すべてのハンドラーで期待されるキーが自動的に更新されます。手動のインターフェースは不要で、ルートとハンドラーのずれも生じません。

route("users/:userId", p => {
  const u = p.userId; // ok
  // const x = p.id; // error: id does not exist
});

パーサーを拡張する

このミニパーサーは、完全な型付きルーターの基礎になります。セグメントごとのヘルパーにさらにテンプレートパターンを追加すれば、型付きの値(数値パラメーター)、ワイルドカード、省略可能なセグメントにも対応できます。再帰的なテンプレート構造は変わりません。

type SegParam2<S extends string> =
  S extends Tpl<':', infer N, '(number)'> ? { [K in N]: number }
  : S extends Tpl<':', infer N> ? { [K in N]: string }
  : {};
// Tpl<...> stands for a backtick template literal type pattern

理解度チェック

ルートパーサーについての理解度を確認します。

まとめ

コンパイル時に動作するルートパーサーを構築しました。

  • 再帰的なテンプレートリテラル推論で各セグメントに一致させます。
  • パラメーターセグメント(:name)は{ name: string }になります。
  • リテラルセグメントは{}になり、交差型の中で消えます。
  • Prettifyによって、結果が見やすい params に平坦化されます。

次はコース25で、tRPC を使ったエンドツーエンドの型安全性を扱います。

よくある質問

「型レベルのミニルートパーサー」レッスンは無料ですか?

はい。「型レベルのミニルートパーサー」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、TypeScript Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 TypeScript Academyコースには全4レッスンが含まれています。

「型レベルのミニルートパーサー」で何を学びますか?

ルートパスを型付きのパラメーターオブジェクトに解析します。 ブラウザで直接実行するハンズオンコードでTypeScript Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

TypeScript Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのTypeScript Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「型レベルのミニルートパーサー」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このTypeScript Academyレッスンでコードを書いて実行できますか?

はい。すべてのTypeScript Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. パーサーコンビネーターの概念
  2. 型レベルの文字列分割
  3. テンプレートリテラルによる解析
  4. 型レベルのミニルートパーサー
← TypeScript Academyに戻る