Effect
An ecosystem of tools to build robust applications in TypeScript
Effect とは
Effect は TypeScript アプリの副作用・エラーハンドリング・並行処理を型安全かつ構造的に扱うための関数型エフェクトシステム。pnpm モノレポで管理される 30+ パッケージ群が「core」「platform」「SQL」「AI」など各レイヤーをカバーする。
30+
パッケージ数
TS 5.4+
必須バージョン
pnpm
パッケージマネージャ
strict
tsconfig 必須フラグ
従来の try/catch や Promise に頼る命令的スタイルを、型で追跡可能な合成可能なエフェクトに置き換えることで「実行時に初めてわかるエラー」をコンパイル時に補足できる。
3 つの設計思想
- 型安全なエラー追跡: エラー型
Eが型シグネチャに現れるため、握りつぶしや未処理エラーがコンパイルで発覚する。 - 依存注入の型安全化: 要求リソース
Rも型で表現し、提供しないまま実行しようとするとコンパイルエラーになる。 - 構造的並行処理: Fiber をファーストクラスで扱い、キャンセル・タイムアウト・リソース解放が自動的かつリーク不可能な形で管理される。
Effect<A, E, R>
Effect
<
A
,
E
,
R
>
A
Success(成功値の型) — エフェクトが成功したときに返す値の型。
Effect<string, …> なら成功時に string が得られる。
E
Error(期待するエラーの型) — 型で追跡されるビジネスエラー。予期しない例外は別枠(Defect)として分離される。
R
Requirements(必要なサービス型) — 実行に必要な依存(Context タグ)のユニオン型。never は依存なしを意味する。
読み方:
Effect<User, NotFound | DbError, Database> は「Database サービスが必要で、成功すると User、失敗すると NotFound か DbError を返すエフェクト」。
基本的なエフェクトの作成と合成
import { Effect } from "effect"
// 成功値を包む
const greet = Effect.succeed("Hello, Effect!")
// 失敗(型付きエラー)
const fail = Effect.fail(new Error("something went wrong"))
// 同期的な副作用を包む
const now = Effect.sync(() => Date.now())
// エフェクトの連鎖(flatMap / pipe)
const program = Effect.gen(function* () {
const msg = yield* greet
const ts = yield* now
return `${msg} at ${ts}`
})
// 実行(R = never のエフェクトだけ run できる)
Effect.runPromise(program).then(console.log)
effect パッケージが提供するモジュール
Effect
副作用・エラーハンドリング・並行処理を構造的に扱うコア抽象。すべての基礎。
Context
型安全な依存注入機構。サービスを直接参照せずに計算へ受け渡す。
Layer
依存関係グラフの宣言的管理。モジュール構成・テスト差し替えに使う。
Fiber
軽量バーチャルスレッド。リソースセーフなキャンセル・並列実行・fork/join を提供。
Stream
非同期・イベント駆動なデータストリームの強力な抽象。バックプレッシャー内蔵。
Schedule
リトライ・リピートポリシーを合成可能なスケジュールで定義。指数バックオフ等。
Scope
リソースのライフサイクル管理。acquire/release を安全に組み合わせる。
Schema
型安全なデータ定義・バリデーション・変換ライブラリ。encode/decode 両対応。
依存関係: Context → Layer → Scope の順で積み上がり、Effect は全モジュールを統合するランタイムとして機能する。Fiber は Effect の並行処理モデルを実装する低レイヤー。
Stream の使用例
import { Stream, Effect } from "effect"
// 1 から 10 までを非同期に流す
const numbers = Stream.range(1, 10)
// フィルタ → マップ → fold
const program = Stream.run(
Stream.pipe(
numbers,
Stream.filter(n => n % 2 === 0),
Stream.map(n => n * n)
),
Sink.collectAll()
)
Effect.runPromise(program).then(console.log)
// [4, 16, 36, 64, 100]
依存解決の流れ
Effect の依存注入は Context.Tag(サービス識別子)→ Layer(実装の組み立て)→ Effect.provide(エフェクトへの注入)という 3 ステップで構成される。
Tag 定義
class Database extends Context.Tag("Database")<Database, { query: … }>(){} でサービス識別子を作る
Layer 作成
Layer.effect(Database, Effect.gen(…)) で実装を包んだ Layer を定義する
Layer 合成
Layer.provide(AppLayer, DatabaseLayer) で依存グラフを宣言的に合成
Effect.provide
Effect.provide(program, AppLayer) で R が never になり runPromise 可能になる
テスト時の差し替え
import { Context, Layer, Effect } from "effect"
// サービスインターフェース
class Database extends Context.Tag("Database")<
Database,
{ readonly query: (sql: string) => Effect.Effect }
>() {}
// 本番実装
const DatabaseLive = Layer.effect(
Database,
Effect.gen(function* () {
// 実際の DB 接続...
return { query: (sql) => Effect.tryPromise(() => pg.query(sql)) }
})
)
// テスト用モック(Layer を差し替えるだけ)
const DatabaseTest = Layer.succeed(Database, {
query: (_sql) => Effect.succeed([{ id: 1, name: "test" }])
})
const program = Effect.gen(function* () {
const db = yield* Database
return yield* db.query("SELECT * FROM users")
})
// テスト
Effect.runPromise(Effect.provide(program, DatabaseTest))
コア・ユーティリティ
| パッケージ | 説明 |
|---|---|
effect | コアパッケージ。Effect / Context / Layer / Fiber / Stream / Schedule / Scope / Schema を包括 |
@effect/typeclass | 関数型プログラミングの型クラス(Functor, Monad 等) |
@effect/vitest | Vitest との統合。Effect を返すテスト関数をネイティブサポート |
@effect/experimental | 実験的 API。安定化前の新機能を先行公開 |
プラットフォーム
| パッケージ | 対象ランタイム |
|---|---|
@effect/platform | クロスプラットフォーム共通抽象(HTTP クライアント・ファイルシステム等) |
@effect/platform-node | Node.js 実装 |
@effect/platform-node-shared | Node.js 共通ユーティリティ |
@effect/platform-bun | Bun 実装 |
@effect/platform-browser | ブラウザ実装 |
SQL ドライバ群
| パッケージ | 対応 DB / ライブラリ |
|---|---|
@effect/sql | SQL 抽象レイヤー(共通インターフェース) |
@effect/sql-pg | postgres.js(PostgreSQL) |
@effect/sql-mysql2 | mysql2(MySQL / MariaDB) |
@effect/sql-mssql | tedious(SQL Server) |
@effect/sql-sqlite-node | better-sqlite3(SQLite / Node) |
@effect/sql-sqlite-bun | bun:sqlite(SQLite / Bun) |
@effect/sql-sqlite-wasm | @sqlite.org/sqlite-wasm(SQLite / WebAssembly) |
@effect/sql-libsql | @libsql/client(Turso / libSQL) |
@effect/sql-clickhouse | ClickHouse |
@effect/sql-d1 | Cloudflare D1 |
@effect/sql-drizzle | Drizzle ORM 統合 |
@effect/sql-kysely | Kysely クエリビルダ統合 |
@effect/sql-sqlite-do | Cloudflare Durable Objects SQLite |
@effect/sql-sqlite-react-native | react-native-quick-sqlite |
その他のパッケージ
| パッケージ | 説明 |
|---|---|
@effect/ai | AI ユーティリティ(モデル非依存の抽象) |
@effect/ai-openai | OpenAI 統合 |
@effect/ai-anthropic | Anthropic Claude 統合 |
@effect/ai-amazon-bedrock | Amazon Bedrock 統合 |
@effect/ai-google | Google AI 統合 |
@effect/cli | 型安全な CLI ビルダー |
@effect/cluster | 分散コンピューティングツール |
@effect/rpc | 型安全な RPC(リモートプロシージャコール) |
@effect/opentelemetry | OpenTelemetry トレーシング統合 |
@effect/workflow | 耐障害性のある永続ワークフロー |
@effect/printer | 汎用プリンタユーティリティ |
@effect/printer-ansi | ANSI カラー対応プリンタ |
インストール
# コアのみ(TypeScript 5.4+, strict: true が必要)
npm install effect
# Node.js プラットフォームも使う場合
npm install effect @effect/platform @effect/platform-node
tsconfig.json の必須設定
{
"compilerOptions": {
"strict": true
// TypeScript 5.4 以上を使用すること
}
}
開発ワークフロー(コントリビュータ向け)
モノレポのビルド・テスト・チェックは pnpm 10.4.0+ で統一管理されている。
pnpm install
→
pnpm codegen
→
pnpm check
→
pnpm test
→
pnpm lint
→
pnpm circular
| コマンド | 説明 |
|---|---|
pnpm codegen | パッケージエントリポイントの再生成(構造変更時) |
pnpm check | TypeScript コンパイルエラーなしを確認 |
pnpm test | 全ユニットテストを実行 |
pnpm test-types | 型レベルテスト(tstyche) |
pnpm lint | コーディングスタイルチェック |
pnpm lint-fix | 自動修正可能な lint エラーを修正 |
pnpm circular | 循環 import の検出 |
pnpm docgen | ドキュメント生成の確認 |
公式・関連
- GitHub: Effect-TS/effect — 本体モノレポ
- effect.website — 公式ドキュメント・チュートリアル
- API リファレンス — effect コアパッケージの全 API
- Discord コミュニティ — 質問・議論の場
- Introduction to Effect(YouTube) — 入門動画
- effect/Schema vs Zod — Schema モジュールと Zod の比較