😈
Tauriで型安全なクライアントを生成する
A
tauri-spectaを使う
※Tauri v1での使い方を書きますが、v2でも問題なくできると思います。
使い方(既存のtauriプロジェクトに追加)
1. tauri-spectaの追加
cargo add specta@1
cargo add tauri-specta@1 --features javascript,typescript
2. #[specta::specta]を適応する
#[tauri::command]
+ #[specta::specta]
fn get_config() -> Result<Config, Error> {
config::get()
}
3. #derive[specta::Type]する
- #[derive(Clone, Serialize, Deserialize, Debug)]
+ #[derive(Clone, Serialize, Deserialize, Debug, specta::Type)]
pub struct Memo {
pub id: i32,
pub content: String,
pub updated_at: String,
pub created_at: String,
pub tags: Vec<models::Tag>,
}
この例だとmodels::Tag
にも#deriveしてあげる必要がある
4. mainでexportする
このようにts::export(collect_types![])に入れます。
collect_types!に入れる時点でエラーが起きた場合、ハンドラの返り値か引数にspecta::Typeがderiveされていないない可能性があります。もう一度3.にいって確認してください。
また以下の例ではproject-root/src/bindings.ts
という形でtsファイルが出力されます。
main関数で行う場合は「main実行」->「src/bindings.ts更新」->「tauriがコードのソース変更検知」->「main実行」の無限ループに陥るため忘れずに.taurignore
に追加してください
testで追加する例
main.rs
#[test]
fn export_bindings(){
+ #[cfg(debug_assertions)]
+ ts::export(
+ collect_types![
+ get_file_text,
+ edit_memo,
+ delete_memo,
+ get_memo,
+ get_memo_list,
+ add_memo,
+ get_config,
+ set_config,
+ save_config,
+ ],
+ "../src/bindings.ts",
+ )
+ .unwrap();
}
感想
バックエンドとフロントエンドのバリデーションをしなくてよいという環境は、罪悪感とともに圧倒的なストレスフリーな環境を用意してくれます。
TypeScriptではResultが使えないのでエラーハンドリングが不安になるのですが、そこはしっかりTypeScriptを書くときはTypeScriptの考え方をして書いたほうがよいという結論になりました。
具体的にはすべてをResult型で考えないよう、tanstack-queryをつかってデータフェッチ時のエラーはonErrorに書いて、それ以外のエラーに関してはどうせテストを書くのでそこで担保、あとはすこしだけneverthrowに頼るという形に落ち着いています。
Discussion