effect-ts
Effect-TSは、TypeScriptで堅牢なアプリケーションを構築するためのライブラリで、try-catchを使わずに型安全なエラー処理や並行処理、依存性注入などを実現し、関数型プログラミングを支援するSkill。
📜 元の英語説明(参考)
Build robust TypeScript applications with Effect — type-safe error handling, concurrency, dependency injection, and streaming. Use when someone asks to "handle errors without try-catch", "type-safe error handling in TypeScript", "dependency injection without classes", "concurrent TypeScript", "Effect library", "replace try-catch with typed errors", or "functional programming in TypeScript". Covers Effect, Layer, Schedule, Stream, and Schema.
🇯🇵 日本人クリエイター向け解説
Effect-TSは、TypeScriptで堅牢なアプリケーションを構築するためのライブラリで、try-catchを使わずに型安全なエラー処理や並行処理、依存性注入などを実現し、関数型プログラミングを支援するSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o effect-ts.zip https://jpskill.com/download/14857.zip && unzip -o effect-ts.zip && rm effect-ts.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/14857.zip -OutFile "$d\effect-ts.zip"; Expand-Archive "$d\effect-ts.zip" -DestinationPath $d -Force; ri "$d\effect-ts.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
effect-ts.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
effect-tsフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Effect-TS
概要
Effect は、エラー、依存関係、および非同期操作を型システムで明示的にする TypeScript ライブラリです。unknown エラーを伴う try-catch の代わりに、すべての関数が何が問題になる可能性があるかを正確に宣言します。グローバルインポートの代わりに、すべての依存関係が型で追跡されます。その結果、テスト、リファクタリング、および推論が容易なコードになります。
どのような時に使うべきか
- 起こりうるすべてのエラーを知ることが重要なビジネスロジック(支払い、認証、データパイプライン)
- テスト可能性を必要とする複雑な依存関係グラフを持つアプリケーション
- 構造化された並行処理を必要とする並行操作(単なる Promise.all ではない)
- データ検証および変換パイプライン
- エラーが発生しやすい try-catch チェーンを型付きエラー処理に置き換える
手順
コアコンセプト: Effect<Success, Error, Requirements>
すべての Effect 値は、3つの型パラメータを持ちます。
- Success: 成功した場合に返すもの
- Error: 生成する可能性のあるエラー
- Requirements: 必要なサービス/依存関係
// core.ts — Effect の基本: 型付きエラーと pipe
import { Effect, pipe } from "effect";
// 型付きエラーを定義する (単なる Error/string ではない)
class UserNotFound {
readonly _tag = "UserNotFound";
constructor(readonly userId: string) {}
}
class DatabaseError {
readonly _tag = "DatabaseError";
constructor(readonly cause: unknown) {}
}
// 型付きエラーで失敗する可能性のある関数
// Success ───┐ Error ──────────────────────────┐ Requirements ─┐
// ▼ ▼ ▼
const getUser = (id: string): Effect.Effect<User, UserNotFound | DatabaseError, never> =>
pipe(
Effect.tryPromise({
try: () => db.user.findUnique({ where: { id } }),
catch: (e) => new DatabaseError(e),
}),
Effect.flatMap((user) =>
user ? Effect.succeed(user) : Effect.fail(new UserNotFound(id))
)
);
// 特定のエラーを処理する
const getUserOrDefault = (id: string) =>
pipe(
getUser(id),
Effect.catchTag("UserNotFound", () =>
Effect.succeed({ id: "default", name: "Guest", email: "guest@example.com" })
)
// DatabaseError はキャッチされない — 型の中で上に伝播する
);
レイヤーによる依存性注入
// services.ts — クラスを使用しない依存性注入
import { Effect, Context, Layer } from "effect";
// サービスインターフェースを定義する
class UserRepo extends Context.Tag("UserRepo")<
UserRepo,
{
findById: (id: string) => Effect.Effect<User | null, DatabaseError>;
create: (data: NewUser) => Effect.Effect<User, DatabaseError>;
}
>() {}
// UserRepo を必要とするビジネスロジック (型で追跡される)
const getUser = (id: string) =>
pipe(
UserRepo, // サービスにアクセスする
Effect.flatMap((repo) => repo.findById(id)),
Effect.flatMap((user) =>
user ? Effect.succeed(user) : Effect.fail(new UserNotFound(id))
)
);
// ^? Effect<User, UserNotFound | DatabaseError, UserRepo>
// ^^^^^^^^ 依存関係が追跡される!
// 本番環境の実装
const UserRepoLive = Layer.succeed(UserRepo, {
findById: (id) =>
Effect.tryPromise({
try: () => prisma.user.findUnique({ where: { id } }),
catch: (e) => new DatabaseError(e),
}),
create: (data) =>
Effect.tryPromise({
try: () => prisma.user.create({ data }),
catch: (e) => new DatabaseError(e),
}),
});
// テスト実装
const UserRepoTest = Layer.succeed(UserRepo, {
findById: (id) => Effect.succeed({ id, name: "Test", email: "test@test.com" }),
create: (data) => Effect.succeed({ id: "new-id", ...data }),
});
// 実際の依存関係で実行する
Effect.runPromise(
pipe(getUser("123"), Effect.provide(UserRepoLive))
);
// テストの依存関係で実行する
Effect.runPromise(
pipe(getUser("123"), Effect.provide(UserRepoTest))
);
並行処理とスケジューリング
// concurrent.ts — Effect を使用した構造化された並行処理
import { Effect, Schedule, Duration } from "effect";
// 制限付きでタスクを並行して実行する
const processItems = (items: string[]) =>
Effect.forEach(items, (item) => processItem(item), {
concurrency: 5, // 最大 5 並行
batching: true, // データベース呼び出しをバッチ処理する
});
// 指数バックオフでリトライする
const resilientFetch = (url: string) =>
pipe(
Effect.tryPromise(() => fetch(url).then((r) => r.json())),
Effect.retry(
Schedule.exponential(Duration.seconds(1)).pipe(
Schedule.compose(Schedule.recurs(3)), // 最大 3 回リトライ
Schedule.jittered, // ランダムなジッターを追加する
)
),
);
// タイムアウト
const withTimeout = pipe(
slowOperation(),
Effect.timeout(Duration.seconds(5)),
);
// レース — 最初に完了したものが勝ち
const fastest = Effect.race(
fetchFromPrimary(),
fetchFromFallback(),
);
Schema — ランタイム検証 + 型推論
// schema.ts — Effect Schema を使用した検証 (Zod を置き換える)
import { Schema } from "effect";
const User = Schema.Struct({
id: Schema.UUID,
name: Schema.String.pipe(Schema.minLength(2), Schema.maxLength(100)),
email: Schema.String.pipe(Schema.pattern(/^[^@]+@[^@]+\.[^@]+$/)),
age: Schema.Number.pipe(Schema.int(), Schema.between(13, 120)),
role: Schema.Literal("user", "admin"),
createdAt: Schema.Date,
});
// 型が推論される — 重複した定義は不要
type User = Schema.Schema.Type<typeof User>;
// デコード (解析 + 検証)
const parseUser = Schema.decodeUnknown(User);
const result = Effect.runSync(parseUser({ id: "...", name: "Kai", email: "kai@example.com", age: 25, role: "user", createdAt: new Date() }));
例
例 1: 支払い処理パイプラインの構築
ユーザープロンプト: 「各失敗ケース(カード拒否、資金不足、不正検出、ネットワークタイムアウト)に対して型付きエラーを持つ支払いフローを構築してください。」
エージェントは、各失敗に対してタグ付きエラー型を定義し、Effect.pipe で支払いパイプラインを構成し、ネットワークエラーに対してリトライを追加し、pro
(原文がここで切り詰められています)
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Effect-TS
Overview
Effect is a TypeScript library that makes errors, dependencies, and async operations explicit in the type system. Instead of try-catch with unknown errors, every function declares exactly what can go wrong. Instead of global imports, every dependency is tracked in the type. The result: code that's easier to test, refactor, and reason about.
When to Use
- Business logic where knowing every possible error matters (payments, auth, data pipelines)
- Applications with complex dependency graphs that need testability
- Concurrent operations that need structured concurrency (not just Promise.all)
- Data validation and transformation pipelines
- Replacing error-prone try-catch chains with typed error handling
Instructions
Core Concept: Effect<Success, Error, Requirements>
Every Effect value has three type parameters:
- Success: what it returns when it works
- Error: what errors it can produce
- Requirements: what services/dependencies it needs
// core.ts — Effect basics: typed errors and pipe
import { Effect, pipe } from "effect";
// Define typed errors (not just Error/string)
class UserNotFound {
readonly _tag = "UserNotFound";
constructor(readonly userId: string) {}
}
class DatabaseError {
readonly _tag = "DatabaseError";
constructor(readonly cause: unknown) {}
}
// Function that can fail with typed errors
// Success ───┐ Error ──────────────────────────┐ Requirements ─┐
// ▼ ▼ ▼
const getUser = (id: string): Effect.Effect<User, UserNotFound | DatabaseError, never> =>
pipe(
Effect.tryPromise({
try: () => db.user.findUnique({ where: { id } }),
catch: (e) => new DatabaseError(e),
}),
Effect.flatMap((user) =>
user ? Effect.succeed(user) : Effect.fail(new UserNotFound(id))
)
);
// Handle specific errors
const getUserOrDefault = (id: string) =>
pipe(
getUser(id),
Effect.catchTag("UserNotFound", () =>
Effect.succeed({ id: "default", name: "Guest", email: "guest@example.com" })
)
// DatabaseError is NOT caught — it propagates up in the type
);
Dependency Injection with Layers
// services.ts — Dependency injection without classes
import { Effect, Context, Layer } from "effect";
// Define a service interface
class UserRepo extends Context.Tag("UserRepo")<
UserRepo,
{
findById: (id: string) => Effect.Effect<User | null, DatabaseError>;
create: (data: NewUser) => Effect.Effect<User, DatabaseError>;
}
>() {}
// Business logic that REQUIRES UserRepo (tracked in type)
const getUser = (id: string) =>
pipe(
UserRepo, // Access the service
Effect.flatMap((repo) => repo.findById(id)),
Effect.flatMap((user) =>
user ? Effect.succeed(user) : Effect.fail(new UserNotFound(id))
)
);
// ^? Effect<User, UserNotFound | DatabaseError, UserRepo>
// ^^^^^^^^ dependency tracked!
// Production implementation
const UserRepoLive = Layer.succeed(UserRepo, {
findById: (id) =>
Effect.tryPromise({
try: () => prisma.user.findUnique({ where: { id } }),
catch: (e) => new DatabaseError(e),
}),
create: (data) =>
Effect.tryPromise({
try: () => prisma.user.create({ data }),
catch: (e) => new DatabaseError(e),
}),
});
// Test implementation
const UserRepoTest = Layer.succeed(UserRepo, {
findById: (id) => Effect.succeed({ id, name: "Test", email: "test@test.com" }),
create: (data) => Effect.succeed({ id: "new-id", ...data }),
});
// Run with real dependencies
Effect.runPromise(
pipe(getUser("123"), Effect.provide(UserRepoLive))
);
// Run with test dependencies
Effect.runPromise(
pipe(getUser("123"), Effect.provide(UserRepoTest))
);
Concurrency and Scheduling
// concurrent.ts — Structured concurrency with Effect
import { Effect, Schedule, Duration } from "effect";
// Run tasks concurrently with a limit
const processItems = (items: string[]) =>
Effect.forEach(items, (item) => processItem(item), {
concurrency: 5, // Max 5 concurrent
batching: true, // Batch database calls
});
// Retry with exponential backoff
const resilientFetch = (url: string) =>
pipe(
Effect.tryPromise(() => fetch(url).then((r) => r.json())),
Effect.retry(
Schedule.exponential(Duration.seconds(1)).pipe(
Schedule.compose(Schedule.recurs(3)), // Max 3 retries
Schedule.jittered, // Add random jitter
)
),
);
// Timeout
const withTimeout = pipe(
slowOperation(),
Effect.timeout(Duration.seconds(5)),
);
// Race — first to complete wins
const fastest = Effect.race(
fetchFromPrimary(),
fetchFromFallback(),
);
Schema — Runtime Validation + Type Inference
// schema.ts — Validation with Effect Schema (replaces Zod)
import { Schema } from "effect";
const User = Schema.Struct({
id: Schema.UUID,
name: Schema.String.pipe(Schema.minLength(2), Schema.maxLength(100)),
email: Schema.String.pipe(Schema.pattern(/^[^@]+@[^@]+\.[^@]+$/)),
age: Schema.Number.pipe(Schema.int(), Schema.between(13, 120)),
role: Schema.Literal("user", "admin"),
createdAt: Schema.Date,
});
// Type is inferred — no duplicate definition
type User = Schema.Schema.Type<typeof User>;
// Decode (parse + validate)
const parseUser = Schema.decodeUnknown(User);
const result = Effect.runSync(parseUser({ id: "...", name: "Kai", email: "kai@example.com", age: 25, role: "user", createdAt: new Date() }));
Examples
Example 1: Build a payment processing pipeline
User prompt: "Build a payment flow with typed errors for each failure case — card declined, insufficient funds, fraud detected, network timeout."
The agent will define tagged error types for each failure, compose the payment pipeline with Effect.pipe, add retry for network errors, and propagate card/fraud errors to the caller with full type information.
Example 2: Testable service with dependency injection
User prompt: "I want to test my business logic without hitting the database."
The agent will define services as Effect Context.Tags, write business logic that depends on service interfaces, create Layer.succeed implementations for both production and test, and run the same logic against fake data in tests.
Guidelines
- Start with
Effect.tryPromise— wrap existing async code first, then gradually adopt more Effect patterns - Tag your errors —
readonly _tag = "ErrorName"enablescatchTagfor precise error handling - Layers for DI — define service interfaces with Context.Tag, implement with Layer
pipeeverything — Effect uses pipe-based composition; avoid nesting- Don't
runPromiseinside Effects — compose Effects together, run once at the entry point - Schema over Zod — Effect Schema integrates with the Effect ecosystem and is more composable
- Concurrency is explicit —
{ concurrency: 5 }not hidden behind promise pools - Learning curve is real — Effect is powerful but complex; introduce gradually to a team
- Effect is a runtime — it manages execution; don't mix with raw Promises inside Effects