effect-typescript
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseEffect TypeScript
Effect TypeScript
Use Effect for application I/O so failures, dependencies, and resource lifetimes are explicit. Adapt a Promise-based driver or SDK once at its boundary; keep internal operations in Effect. Run Effects at the caller or runtime boundary that requires a Promise or starts the program. Pure transformations remain plain TypeScript.
Follow codebase-design for the shared readability rules and examples. Keep related queries, bindings, decoding, and private helpers with their adapter; extract only when it hides complexity or enables useful reuse. Operations own completion, errors, and cleanup, or explicitly hand that responsibility to their caller.
This skill adapts Effect guidance to darkmatter conventions: use Bun commands instead of pnpm for darkmatter projects, and prefer Alchemy for deployable infrastructure.
在应用I/O中使用Effect,以便明确故障、依赖项和资源生命周期。在边界处一次性适配基于Promise的驱动程序或SDK;内部操作保持使用Effect。在需要Promise或启动程序的调用方或运行时边界处运行Effect。纯转换仍使用普通TypeScript。
通用可读性规则和示例请遵循codebase-design。将相关查询、绑定、解码和私有辅助函数与其适配器放在一起;仅当这样做能隐藏复杂性或实现有用的复用时才进行提取。操作自身负责完成、错误处理和清理,或者明确将该责任移交给调用方。
本技能根据darkmatter约定调整了Effect指南:darkmatter项目使用Bun命令而非pnpm,可部署基础设施优先使用Alchemy。
Bootstrap
快速上手
If is not on in your , install it: . Prefer the repository's pinned Effect source or installed package for API details; use a version-matching checkout when neither is available.
effect-solutions$PATHbun add -g effect-solutions@latestFor new Darkmatter TypeScript repos, align dependencies with the
root catalog. Use Effect 4, ,
, , and ; prefer the
Effect agent APIs () before inventing agent loop contracts.
darkmatter/template@effect/tsgo@effect/vitest@effect/platform-buneffect-solutionseffect/unstable/ai如果你的中没有,请安装它:。API详情优先参考仓库固定的Effect源码或已安装的包;如果两者都不可用,请使用版本匹配的检出版本。
$PATHeffect-solutionsbun add -g effect-solutions@latest对于新的Darkmatter TypeScript仓库,请将依赖与根catalog对齐。使用Effect 4、、、和;在自行设计Agent循环契约之前,优先使用Effect Agent API()。
darkmatter/template@effect/tsgo@effect/vitest@effect/platform-buneffect-solutionseffect/unstable/aiGuidelines
指南
- Refer to the best practices recommended by the CLI when writing effectful code.
effect-solutions - If you need more specific information, there's a local checkout of the effect source in the reference directory.
- 编写有副作用的代码时,请参考CLI推荐的最佳实践。
effect-solutions - 如果你需要更具体的信息,参考目录中有effect源码的本地检出版本。
When to use
适用场景
- Meaningful I/O is involved and the work benefits from explicit failure, dependency, resource, retry, validation, or testing boundaries.
- The code already uses Effect and you are adding or reviewing Effect code.
- You are deciding whether a TypeScript/Bun feature should use Effect.
- The task involves external APIs, databases, queues, workers, CLIs, schedules, retries, config, secrets, structured logging, runtime validation, resource cleanup, or concurrent workflows.
- You need typed domain errors rather than unstructured thrown exceptions.
- You need swappable live/test implementations through services and Layers.
- You are deploying TypeScript infrastructure or workers and need Alchemy-aware conventions.
- 涉及重要I/O,且工作能从明确的故障、依赖、资源、重试、验证或测试边界中受益。
- 代码已使用Effect,且你正在添加或审查Effect代码。
- 你正在决定某个TypeScript/Bun功能是否应该使用Effect。
- 任务涉及外部API、数据库、队列、Worker、CLI、调度、重试、配置、密钥、结构化日志、运行时验证、资源清理或并发工作流。
- 你需要类型化的领域错误,而非非结构化的抛出异常。
- 你需要通过服务和Layer实现可替换的线上/测试实现。
- 你正在部署TypeScript基础设施或Worker,且需要符合Alchemy规范的约定。
When NOT to use
不适用场景
- A small one-off script can be obvious plain TypeScript: read one file, transform pure data, write one file, no retries, no injected dependencies, no long-lived resources. As requirements grow, adopt Effect where I/O needs it and extract coherent responsibilities. A line count alone does not justify scattering the implementation; retain configured limits with documented, targeted exceptions where needed.
- Pure functions, simple data mappers, UI-local state, or tiny glue code do not need Effect wrappers.
- A project has no Effect dependency and the feature does not benefit from typed errors, Layers, resource safety, retries, or observability.
- The team only needs a tactical fix in plain async code. Do not introduce Effect as a drive-by refactor.
- You cannot explain the service/layer/error/testing shape. Stop and design that first instead of sprinkling calls everywhere.
Effect.runPromise
- 小型一次性脚本可以直接使用普通TypeScript:读取一个文件、转换纯数据、写入一个文件,无需重试、注入依赖或长生命周期资源。随着需求增长,在I/O需要的地方采用Effect,并提取连贯的职责。仅靠行数不能成为分散实现的理由;在需要时保留配置好的限制,并记录有针对性的例外情况。
- 纯函数、简单数据映射器、UI本地状态或微型粘合代码不需要Effect包装。
- 项目没有Effect依赖,且该功能无法从类型化错误、Layer、资源安全、重试或可观测性中受益。
- 团队只需要对普通异步代码进行临时修复。不要在顺手重构时引入Effect。
- 你无法解释服务/Layer/错误/测试的结构。请先停下来设计这些,而不是到处随意添加调用。
Effect.runPromise
Darkmatter Conventions
Darkmatter约定
- Use Bun commands: ,
bun install,bun test,bun run <script>.bunx <tool> - Translate upstream examples mechanically. Example:
pnpmbecomespnpm test file.test.tswhen the project uses Bun test, orbun test file.test.tswhen test is a package script.bun run test file.test.ts - Prefer Bun runtime packages where relevant, such as and
@effect/platform-bunfor Bun entrypoints.BunRuntime.runMain - Prefer Alchemy for deployable infrastructure. Put infra in , create resources with Alchemy, bind them to workers/services, and build up an
alchemy.run.ts. Refer to the alchemy skill if writing alchemy code or adding new infra code.Alchemy.Stack - Prefer Postgres for application data. Use Kysely + for query-heavy TypeScript adapters, or Effect SQL Postgres when persistence is exposed as Effect services and Layers. D1/sqlite are exceptions for narrow edge-local state, demos, or an explicit project decision.
pg - Configuration comes from named config files () read through
config/<name>.json. There is no selector flag: an interactive run picks from a list (Config), a non-interactive run readsPrompt.selector fails listing the names. Env vars and flags are per-key overrides chained withAPP_CONFIG; secrets come from theConfigProvider.orElsesibling via.sops.json. Do not design the setup of a program as a list of flags or env vars. See ADR-0014.effect-sops
- 使用Bun命令:、
bun install、bun test、bun run <script>。bunx <tool> - 机械地转换上游示例。例如:当项目使用Bun test时,
pnpm变为pnpm test file.test.ts;当test是包脚本时,则变为bun test file.test.ts。bun run test file.test.ts - 相关场景下优先使用Bun运行时包,例如Bun入口点使用和
@effect/platform-bun。BunRuntime.runMain - 可部署基础设施优先使用Alchemy。将基础设施代码放在中,使用Alchemy创建资源,将它们绑定到Worker/服务,并构建
alchemy.run.ts。编写Alchemy代码或添加新的基础设施代码时,请参考Alchemy技能。Alchemy.Stack - 应用数据优先使用Postgres。对于查询密集型TypeScript适配器,使用Kysely + ;当持久化以Effect服务和Layer形式暴露时,使用Effect SQL Postgres。D1/sqlite仅用于有限的边缘本地状态、演示或明确的项目决策等例外情况。
pg - 配置来自通过读取的命名配置文件(
Config)。没有选择器标志:交互式运行从列表中选择(config/<name>.json),非交互式运行读取Prompt.select,否则列出可用名称并失败。环境变量和标志是通过APP_CONFIG链接的逐键覆盖项;密钥通过ConfigProvider.orElse从同级的effect-sops文件中获取。不要将程序的设置设计为一系列标志或环境变量。参见ADR-0014。.sops.json
Typed RPC with effect-orpc
使用effect-orpc实现类型化RPC
Use when a service needs typed RPC procedures, a generated or
shared oRPC client contract, Effect service injection, and typed Effect errors.
Use raw Effect or Hono when the route is simple HTTP, a webhook,
static content, streaming/SSE, or a framework adapter where an RPC contract
would add ceremony without a client-facing type contract.
effect-orpcHttpRouterWhen adding , pin the Effect 4 build in the catalog:
effect-orpcjson
{
"workspaces": {
"catalog": {
"effect-orpc": "1.0.0-effect-v4.8",
"@orpc/server": "^1.13.0",
"@orpc/client": "^1.13.0",
"@orpc/contract": "^1.13.0",
"@orpc/shared": "^1.13.0"
}
}
}Do not install ; npm is the Effect 3 line
(). The dist-tag currently resolves to
, whose peers require Effect 4
() and the oRPC packages above.
effect-orpc@latestlatest0.5.0effect-v41.0.0-effect-v4.8effect >=4.0.0-beta.98Prefer these upstream shapes:
- Build procedures from or
eos.provide(AppLayer). Use a caller-ownedmakeEffectORPC(runtime)when a scoped layer should be acquired once and disposed during application shutdown, such as a Postgres pool, telemetry SDK, or shared cache.ManagedRuntime - Write handlers and Effect middleware with and generator
.effect(function* (...) { ... })so service requirements, spans, cancellation, and errors stay in Effect..use(function* ({ next }) { return yield* next(...) }) - Model HTTP errors with and pass those classes to
ORPCTaggedError/.errors(...); do not duplicate the same error contract as unrelated thrown exceptions and response schemas.eoc.errors(...) - Use when a repo is contract-first and already owns an oRPC contract. Contract leaves keep the declared input, output, and error surface while adding
implementEffect(contract, layerOrRuntime)handlers..effect(...) - Import only when Node
effect-orpc/nodeis needed to preserve FiberRefs across native oRPC middleware or framework boundaries. UseAsyncLocalStoragewhen request-local FiberRef state is created outside the oRPC pipeline and must be visible inside handlers.withFiberContext(() => next())
当服务需要类型化RPC过程、生成的或共享的oRPC客户端契约、Effect服务注入以及类型化Effect错误时,使用。当路由是简单HTTP、Webhook、静态内容、流式传输/SSE,或者是框架适配器(此时RPC契约会增加不必要的繁琐流程,且没有面向客户端的类型契约)时,使用原生Effect 或Hono。
effect-orpcHttpRouter添加时,请在catalog中固定Effect 4版本:
effect-orpcjson
{
"workspaces": {
"catalog": {
"effect-orpc": "1.0.0-effect-v4.8",
"@orpc/server": "^1.13.0",
"@orpc/client": "^1.13.0",
"@orpc/contract": "^1.13.0",
"@orpc/shared": "^1.13.0"
}
}
}不要安装;npm的标签对应的是Effect 3系列()。 dist-tag当前解析为,其对等依赖要求Effect 4()和上述oRPC包。
effect-orpc@latestlatest0.5.0effect-v41.0.0-effect-v4.8effect >=4.0.0-beta.98优先使用以下上游模式:
- 从或
eos.provide(AppLayer)构建过程。当作用域Layer只需获取一次并在应用关闭时释放时(例如Postgres连接池、遥测SDK或共享缓存),使用调用方拥有的makeEffectORPC(runtime)。ManagedRuntime - 使用和生成器形式的
.effect(function* (...) { ... })编写处理器和Effect中间件,以便服务需求、span、取消和错误都保持在Effect体系内。.use(function* ({ next }) { return yield* next(...) }) - 使用建模HTTP错误,并将这些类传递给
ORPCTaggedError/.errors(...);不要将相同的错误契约重复实现为不相关的抛出异常和响应模式。eoc.errors(...) - 当仓库采用契约优先且已经拥有oRPC契约时,使用。契约叶子节点在添加
implementEffect(contract, layerOrRuntime)处理器的同时,保留声明的输入、输出和错误暴露面。.effect(...) - 仅当需要Node的来跨原生oRPC中间件或框架边界保留FiberRef时,才导入
AsyncLocalStorage。当请求本地的FiberRef状态在oRPC管道之外创建,且必须在处理器内部可见时,使用effect-orpc/node。withFiberContext(() => next())
Package layout reference
包布局参考
See effect-package-map.md for domain ownership, public package entries, adapters, workflows, tests, and runtime boundaries. It describes responsibilities rather than prescribing a file for each function or Effect export.
有关领域所有权、公共包入口、适配器、工作流、测试和运行时边界,请参见effect-package-map.md。它描述的是职责划分,而非为每个函数或Effect导出指定文件。
Upstream Reference
上游参考
Use or CLI when you need current Effect source or examples instead of relying on memory:
reference/effecteffect-solutions- — upstream repository rules, including pnpm validation commands, generated barrels, changesets, and
reference/effect/AGENTS.mdconventions.it.effect - — core library source and tests.
reference/effect/packages/effect/ - and
reference/effect/packages/platform-bun/— runtime/platform examples.reference/effect/packages/platform-node/ - — Effect-aware Vitest helpers.
reference/effect/packages/vitest/
If the submodule is missing in a fresh checkout, initialize it before using local references:
bash
git submodule update --init skills/effect-typescript/reference/effectFor darkmatter application work, treat the submodule as read-only and translate upstream pnpm commands to the repo's package manager. For direct upstream Effect contributions, follow exactly; do not apply darkmatter Bun defaults inside the upstream repo.
reference/effect/AGENTS.md当你需要最新的Effect源码或示例而不是依赖记忆时,使用或 CLI:
reference/effecteffect-solutions- — 上游仓库规则,包括pnpm验证命令、生成的barrels(桶文件)、changesets和
reference/effect/AGENTS.md约定。it.effect - — 核心库源码和测试。
reference/effect/packages/effect/ - 和
reference/effect/packages/platform-bun/— 运行时/平台示例。reference/effect/packages/platform-node/ - — 支持Effect的Vitest辅助工具。
reference/effect/packages/vitest/
如果在新检出的代码中缺少子模块,请在使用本地参考之前初始化它:
bash
git submodule update --init skills/effect-typescript/reference/effect对于darkmatter应用开发,请将子模块视为只读,并将上游pnpm命令转换为仓库使用的包管理器命令。对于直接向上游Effect贡献代码,请严格遵循;不要在上游仓库中应用darkmatter的Bun默认配置。
reference/effect/AGENTS.mdJSON Encoding & Decoding
JSON编码与解码
Use to parse JSON strings and validate them with your schema in one step. This combines + schema decoding in one step, and + schema encoding for the reverse:
Schema.fromJsonStringJSON.parseJSON.stringifytypescript
import { Effect, Schema } from "effect"
const Row = Schema.Literals(["A", "B", "C", "D", "E", "F", "G", "H"])
const Column = Schema.Literals(["1", "2", "3", "4", "5", "6", "7", "8"])
class Position extends Schema.Class<Position>("Position")({
row: Row,
column: Column,
}) {}
class Move extends Schema.Class<Move>("Move")({
from: Position,
to: Position,
}) {}
// fromJsonString combines JSON.parse + schema decoding
// MoveFromJson is a schema that takes a JSON string and returns a Move
const MoveFromJson = Schema.fromJsonString(Move)
const program = Effect.gen(function* () {
// Parse and validate JSON string in one step
// Use MoveFromJson (not Move) to decode from JSON string
const jsonString = '{"from":{"row":"A","column":"1"},"to":{"row":"B","column":"2"}}'
const move = yield* Schema.decodeUnknownEffect(MoveFromJson)(jsonString)
yield* Effect.log("Decoded move", move)
// Encode to JSON string in one step (typed as string)
// Use MoveFromJson (not Move) to encode to JSON string
const json = yield* Schema.encodeEffect(MoveFromJson)(move)
return json
})使用可以一步完成JSON字符串解析和Schema验证。它将 + Schema解码合并为一步,反向则合并了 + Schema编码:
Schema.fromJsonStringJSON.parseJSON.stringifytypescript
import { Effect, Schema } from "effect"
const Row = Schema.Literals(["A", "B", "C", "D", "E", "F", "G", "H"])
const Column = Schema.Literals(["1", "2", "3", "4", "5", "6", "7", "8"])
class Position extends Schema.Class<Position>("Position")({
row: Row,
column: Column,
}) {}
class Move extends Schema.Class<Move>("Move")({
from: Position,
to: Position,
}) {}
// fromJsonString combines JSON.parse + schema decoding
// MoveFromJson is a schema that takes a JSON string and returns a Move
const MoveFromJson = Schema.fromJsonString(Move)
const program = Effect.gen(function* () {
// Parse and validate JSON string in one step
// Use MoveFromJson (not Move) to decode from JSON string
const jsonString = '{"from":{"row":"A","column":"1"},"to":{"row":"B","column":"2"}}'
const move = yield* Schema.decodeUnknownEffect(MoveFromJson)(jsonString)
yield* Effect.log("Decoded move", move)
// Encode to JSON string in one step (typed as string)
// Use MoveFromJson (not Move) to encode to JSON string
const json = yield* Schema.encodeEffect(MoveFromJson)(move)
return json
})