ts-rsでTypeScriptのテンプレートリテラル型を活用したタイプセーフなパスパラメータの実装
はじめに
TauriアプリケーションでAxumをバックエンドとして使用する際、フロントエンドからのリクエストパスの型安全性を確保することは重要です。特に、Axumのルーターで受け付けるパスパラメータとフロントエンドのTypeScriptコードの間で型の整合性を保つことで、実行時エラーを防ぐことができます。
本記事では、ts-rsを使用してTypeScriptのテンプレートリテラル型を活用し、以下のような利点を持つパスパラメータの実装方法を紹介します:
- Axumルーターで受け付けるパスパターンをTypeScript側で型として表現
- コンパイル時の型チェックによる早期エラー検出
- フロントエンド・バックエンド間での型の一貫性確保
例えば、以下のようなAxumのルート定義があるとき:
let app = Router::new()
.route("/page/:id", get(handler))
.route("/api/v1/:resource", post(api_handler));
この記事で紹介する実装方法により、フロントエンド側で以下のような型チェックを実現できます:
// コンパイル時に型チェックが効く
const { path }: Page = { path: "page/123" }; // OK
const { path }: Page = { path: "page/hoge" }; // コンパイルエラー
const { path }: Page = { path: "invalid/123" }; // コンパイルエラー
環境
[dependencies]
ts-rs = "10.1.0"
serde = { version = "1.0", features = ["derive"] }
実装方法
1. Rust側の型定義
use ts_rs::TS;
use serde::{Serialize, Deserialize};
#[derive(TS, Serialize, Deserialize)]
#[ts(export, export_to = "page.ts")]
pub struct Page {
#[ts(type = "`page/${number}`")] // テンプレートリテラル型の定義
pub path: String,
}
2. 生成されるTypeScript型定義
export type Page = { path: `page/${number}`, };
3. 使用例
// 正常なケース
const { path }: Page = { path: "page/6" }; // OK
// コンパイルエラーとなるケース
const { path1 }: Page = { path: "page/hoge" }; // Error: Type 'string' is not assignable to type '`page/${number}`'
const { path2 }: Page = { path: "invalid/6" }; // Error: Type 'string' is not assignable to type '`page/${number}`'
詳細な説明
テンプレートリテラル型とは
TypeScriptのテンプレートリテラル型は、文字列の形式を型レベルで制約することができる機能です。この機能を使うことで、以下のような利点があります:
- コンパイル時の型チェック
- IDEによる入力補完
- 実行時エラーの防止
ts-rsでの実装のポイント
-
属性の設定
#[ts(export, export_to = "page.ts")]
-
export
: TypeScript型定義を生成 -
export_to
: 出力先ファイルを指定
-
-
型定義のカスタマイズ
#[ts(type = "`page/${number}`")]
- テンプレートリテラル型を直接指定
- TypeScriptの型システムを活用
活用例
// 複数のパスパラメータパターン
#[derive(TS, Serialize, Deserialize)]
#[ts(export)]
pub struct ApiPath {
#[ts(type = "`api/v${number}/${string}`")]
pub path: String,
}
// オプショナルパラメータ
#[derive(TS, Serialize, Deserialize)]
#[ts(export)]
pub struct UserPath {
#[ts(type = "`users/${number}/${string?}`")]
pub path: String,
}
注意点
-
シリアライズ/デシリアライズ
- Rust側では通常の
String
として扱われる - TypeScript側でのみ型制約が適用される
- Rust側では通常の
-
型の制約
- コンパイル時の型チェックのみ
- 実行時のバリデーションは別途必要
まとめ
ts-rsとTypeScriptのテンプレートリテラル型を組み合わせることで、以下が実現できます:
- 型安全なパスパラメータの実装
- コンパイル時の型チェック
- 開発時の補完サポート
これにより、RustとTypeScript間のより安全なインターフェース設計が可能になります。