AIにコードを書かせるならEffectを全面採用する

  • Effect
  • effect-tsgo
  • TypeScript

AIが書くコードに繰り返し出る欠陥

AIエージェントが書いたTypeScriptには、次の欠陥が繰り返し出てくる。

欠陥コード
timeout がないfetch を時間制限なしで呼ぶ
並列数が無制限Promise.all に数千件の配列を渡す
エラーを握りつぶすcatch (e) {}
キャンセルが伝わらないAbortSignal を途中で渡し忘れる
retry が壊れている手書きの retry ループが止まらない

どれも、関数が何で失敗するか、何に依存するか、リソースをいつ解放するかが型に現れないため、型チェックを通ってしまう。

Effectは失敗と依存を型に載せる

Effect(effect-ts)の Effect<A, E, R> は3つの型引数を持つ。

型引数表すもの
A成功時の値
E起こりうるエラー
R実行に必要な依存

2026年10月時点の既定はv4で、HTTP、SQL、CLI、AI、Schema、Config、Stream が同じコアの上にあり、依存は単一パッケージになった1。

Result型ライブラリとの違い

エラーを型に載せるだけなら、neverthrow や ts-results などのResult型ライブラリでもできる。違いは2つある。

遅延評価とruntime

Result型は実行済みの結果を表す。Effectは遅延評価されるプログラムの記述で、runtime が実行する。そのため実行の仕方をコンビネータとして合成できる。

欠陥Effectでの書き方
timeout がないEffect.timeout
並列数が無制限{ concurrency: 5 }
retry が壊れているEffect.retry に Schedule を渡す
失敗時にリソースが残るEffect.acquireRelease
キャンセルが伝わらないFiber への interrupt が自動で伝搬する

冒頭の欠陥は、どれもこれらで書ける処理を手書きして壊したものだ。

依存を表す R

Result型には依存を表す場所がない。Effectでは、データベースやHTTPクライアントが必要な処理はそれが R に現れ、Layer を提供し忘れると型エラーになる。テストでは Layer を差し替える。

AIにとっての利点

エージェントは、失敗と依存を、実装を読まずにシグネチャから知れる。エラーの種類を追加すれば、処理漏れの箇所が型エラーとしてすべて出る。型エラーはテストや実行時例外と違って決定的ですぐ返るので、修正ループが確実に回る。公式ドキュメントもこの点を強みに挙げている1。

遅延評価の落とし穴

yield せず代入もしていない Effect は実行されない。

const program = Effect.gen(function* () {
  saveUser(user); // yield* がないので保存は実行されない
  yield* sendWelcomeMail(user);
});

これは型エラーにならない。Promise の await 忘れと違い、処理自体が起きない。

effect-tsgoが正しい使い方を強制する

正体

パッケージ名は @effect/tsgo で、Go製のTypeScriptコンパイラ TypeScript-Go に Effect Language Service を組み込んだビルドである2。

機能内容
診断floating Effect、処理されていないエラー型、満たされていない依存など50種類以上
refactorasync/await から Effect への変換、Effect.gen と Effect.fn の相互変換など

lintとの違い

診断は通常のTypeScriptの診断として出力される3。

実行される工程回避できるか
eslint-plugin-neverthrow型チェックと別の lint設定から外せるし、実行し忘れもある
effect-tsgo型チェックそのもの型チェックを通す限り避けられない

エディタ、CI、エージェントの実行のどこでも同じ診断が出る。ライブラリは使われなければ意味がないが、Effectは正しく使わせる手段を公式が提供している。

例外を作らない

CLIや小さなスクリプトだけResult型とlintにすると、エラー処理の書き方が2つになる。エージェントはどちらで書くかを毎回判断して誤る。両者の境界では型が途切れ、そこは effect-tsgo の診断の外になる。

全面採用は維持しやすい。Effectを返す関数を呼ぶ側は、Effectで受けないと値を取り出せない。R を満たすには Layer を書く必要がある。既存の async/await は effect-tsgo の refactor で移行できる。

コスト

項目内容
API大きく、v3からv4でも変わっている。LLMの学習データの大半はv3以前なので、存在しないAPIや古い書き方が出やすい
型エラー長く、コンテキストを消費する
成熟度effect-tsgo はまだAlphaで、リリース間の破壊的変更が想定されている
導入条件typescript 7以上のネイティブ版が必要で、素の tsgo と language server を併用すると診断が重複する2

出典

  1. Effect v4 docs ↩ ↩2

  2. Effect-TS/tsgo ↩ ↩2

  3. Effect v4 Devtools ↩