mcpField
E
effect-ts

effect-ts

github.com/pedronauck开发647256 次安装1 天前更新
代码仓库

关于 effect-ts

面向 Effect-TS 项目的开发指南技能,参考资料涵盖核心 API、模式、测试和工具。它明确规定类型安全的 Effect、Schema 建模、模式匹配与验证约定。

主要功能

  1. 01按主题划分的参考文档
  2. 02Effect 组合与数据类型指南
  3. 03带穷尽检查的标签联合匹配
  4. 04错误与资源处理模式
  5. 05测试及验证清单

适用场景

  1. 01按统一规范实现 Effect-TS 功能
  2. 02建模类型化 Schema 与错误
  3. 03审查标签联合分支处理
  4. 04准备测试和项目检查

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/pedronauck/skills --skill effect-ts

安装后在 Claude Code 中输入/effect-ts确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
api-platform-observability.md
batching-and-caching.md
class-patterns.md
concurrency-and-resources.md
construction-and-style.md
control-flow-and-runtime.md
core-patterns.md
data-and-testing.md
data-types.md
error-handling-patterns.md
foundations.md
library-development-patterns.md
pattern-matching.md
quality-tooling-and-resources.md
schema-errors-config.md
schema-transforms-and-filters.md
sink.md
streams-deep-dive.md
testing-patterns.md

详细介绍

Effect-TS 开发指南

本项目中 Effect-TS 的指南、模式和最佳实践。

参考文档

编写代码前,请先阅读相关参考文档。references/core-patterns.md 是主索引。

参考文档主题
references/foundations.md设置、导入、TypeScript 配置
references/construction-and-style.mdEffect.gen、pipe、Effect.fn、Effect.fnUntraced
references/schema-errors-config.mdSchema 建模、错误、配置、重试
references/pattern-matching.mdMatch.type、Match.value、Match.tag、Match.exhaustive — 标记联合类型必须使用
references/control-flow-and-runtime.mdEffect.if、Effect.when、循环、runSync、runPromise、ManagedRuntime
references/data-types.md所有数据类型:Option、Either、Data、Exit、Cause、Duration、DateTime、BigDecimal、Chunk、HashSet、Redacted
references/data-and-testing.mdOption/Either/Array 快速参考、@effect/vitest 设置
references/concurrency-and-resources.md并发、Scope、终结器、资源
references/streams-deep-dive.md创建、操作、分组、分区、广播、缓冲、限流、错误处理
references/sink.mdSink 构造器、收集、折叠、操作、并发、剩余项、Stream.transduce
references/batching-and-caching.md请求批处理(RequestResolver)、cachedWithTTL
references/schema-transforms-and-filters.mdSchema.transform、Schema.filter、细化
references/api-platform-observability.mdHttpApi、日志、追踪、跨度
references/class-patterns.mdContext.Tag 服务模式、层、记忆化、测试
references/error-handling-patterns.mdData.TaggedError、Schema.TaggedError、错误组合、恢复
references/library-development-patterns.md禁止的模式、Effect.fn 与 Effect.fnUntraced、资源管理
references/testing-patterns.md使用 assert、TestClock、服务模拟的 @effect/vitest
references/quality-tooling-and-resources.md反模式、验证清单、包

核心原则

  1. Effect 不只是用于异步操作 — 对任何可能失败的操作都使用 Effect
  2. 不可变性 — 使用 Effect 的不可变数据结构(Data、Chunk、HashSet)
  3. 类型安全 — 在类型中追踪错误。错误通道中不得使用 any 或 unknown
  4. 组合 — 通过组合小型 Effect 来构建程序
  5. Schema 优先 — 使用带品牌类型的 Schema 定义数据模型
  6. 模式匹配 — 对标记联合类型的所有分支判断都使用 Match(绝不要针对 _tag 使用 switch/if-else)
  7. Effect 数据类型 — 使用 Option(而不是 null)、Either(而不是临时方案)、Duration(而不是原始毫秒值)、DateTime(而不是 Date)、BigDecimal(而不是浮点数)、Redacted(用于机密信息)。请参阅 references/data-types.md

快速参考

导入约定
import * as Context from "effect/Context";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Schema from "effect/Schema";
import * as Match from "effect/Match";
import * as Option from "effect/Option";
import * as Either from "effect/Either";
import * as Data from "effect/Data";
import * as Duration from "effect/Duration";
import { pipe } from "effect/Function";
// Also: DateTime, BigDecimal, Chunk, HashSet, Exit, Cause, Redacted
Effect.gen 与 pipe 和 Effect.fn
// Effect.gen — complex logic with branching
Effect.gen(function* () {
  const user = yield* fetchUser(id);
  if (user.isAdmin) yield* logAdminAccess(user);
  return user;
});

// pipe — linear transformations
pipe(fetchData(), Effect.map(transform), Effect.flatMap(save));

// Effect.fn — traced reusable functions (public API)
const processUser = Effect.fn("processUser")(function* (userId: string) {
  const user = yield* getUser(userId);
  return yield* processData(user);
});
错误处理

进程内区分错误时使用 Data.TaggedError,需要可序列化的错误时使用 Schema.TaggedError。请参阅 references/error-handling-patterns.md。

export class NotFoundError extends Data.TaggedError("NotFoundError")<{
  id: string;
}> {}

// Recovery
pipe(riskyOp, Effect.catchTag("NotFoundError", (e) => Effect.succeed(null)));
模式匹配(标记联合类型必须使用)

始终使用 Match — Match.exhaustive 会在编译时捕获遗漏的分支。请参阅 references/pattern-matching.md。

// Match.type — reusable matcher function
const handle = Match.type<Status>().pipe(
  Match.tag("Pending", (s) => `Pending since ${s.requestedAt}`),
  Match.tag("Approved", (s) => `Approved by ${s.approvedBy}`),
  Match.exhaustive // Compile error if any variant is missing
);

// Match.valueTags — shorthand for immediate matching
Match.valueTags(status, {
  Pending: (s) => `Pending since ${s.requestedAt}`,
  Approved: (s) => `Approved by ${s.approvedBy}`,
});
服务模式(Context.Tag)

有关包含工厂方法和层的完整模式,请参阅 references/class-patterns.md。

export class MyService extends Context.Tag("@myapp/MyService")<
  MyService,
  { readonly find: (id: string) => Effect.Effect<Result, NotFoundError> }
>() {
  static readonly layer = Layer.effect(MyService, Effect.gen(function* () {
    const db = yield* Database;
    return MyService.of({ find: MyService.createFind(db) });
  }));
}
测试

重要:对 it.effect 使用来自 @effect/vitest 的 assert。不要在 it.effect 中使用 expect。请参阅 references/testing-patterns.md。

import { assert, describe, it } from "@effect/vitest";

it.effect("processes data", () =>
  Effect.gen(function* () {
    const result = yield* processData("input");
    assert.strictEqual(result, "expected");
  }).pipe(Effect.provide(MyService.testLayer))
);

禁止的模式

// NEVER: try-catch in Effect.gen — use Effect.exit instead
Effect.gen(function* () {
  try { yield* someEffect } catch (e) { } // WRONG — will never catch
});

// NEVER: Type assertions
const value = something as any;   // FORBIDDEN
const value = something as never; // FORBIDDEN

// NEVER: Missing return on terminal yield
Effect.gen(function* () {
  if (bad) { yield* Effect.fail("err") } // Missing return!
});

// NEVER: switch/if-else on _tag — use Match instead
switch (status._tag) { /* no exhaustiveness checking! */ }

// NEVER: Effect.runSync inside Effects
Effect.gen(function* () { Effect.runSync(sideEffect) }); // Loses error tracking

// NEVER: Native JS where Effect data types exist
const x: string | null = null;              // Use Option<string>
const delay = 5000;                         // Use Duration.seconds(5)
const now = new Date();                     // Use DateTime.now or DateTime.unsafeNow()
const price = 0.1 + 0.2;                   // Use BigDecimal for precision
const secret = "sk-1234";                   // Use Redacted.make("sk-1234")

// NEVER: expect with it.effect
it.effect("test", () => Effect.gen(function* () {
  expect(result).toBe(value) // WRONG — use assert.strictEqual
}));

// NEVER: Inline layers (breaks memoization)
Layer.provide(Postgres.layer({ url })) // Store in constant instead

验证清单

  • 导入使用 import * as Module from "effect/Module"
  • 复杂逻辑使用 Effect.gen,线性逻辑使用 pipe,公共 API 使用 Effect.fn
  • 所有 _tag 分支都使用 Match 和 Match.exhaustive
  • 为领域原语(ID、Email)使用品牌类型
  • 错误:使用 Data.TaggedError(用于区分)或 Schema.TaggedError(可序列化)
  • 错误通道中不使用 any/unknown,不使用类型断言
  • 不在 Effect.gen 中使用 try-catch — 使用 Effect.exit
  • 对终止效果(Effect.fail、Effect.interrupt)使用 return yield*
  • 服务:使用带静态工厂方法和 Effect.fn 跟踪的 Context.Tag
  • 层:使用 Layer.merge/Layer.provide,在常量中定义参数化层
  • 资源:使用 Effect.acquireRelease 或 Effect.scoped
  • 可空值使用 Option,同步成功/失败使用 Either
  • 时间值使用 Duration,日期使用 DateTime(而不是 Date)
  • 金融/精确计算使用 BigDecimal,机密使用 Redacted
  • 结构相等比较使用 Data.struct/Data.Class,集合使用 HashSet
  • 使用 Clock.currentTimeMillis,而不是 Date.now()
  • 测试:使用来自 @effect/vitest 的 assert(而不是 expect)和 it.effect
  • 运行 pnpm run typecheck 和 pnpm run test

参考实现

参阅 /packages/looper/src/data/api-client/api-client.ts,了解 Context.Tag 服务模式。

Effect Solutions CLI

pnpm exec effect-solutions list              # List all topics
pnpm exec effect-solutions show <slug...>    # Read topics
pnpm exec effect-solutions search <term>     # Search by keyword

评论

支持 Markdown