{"id":"e280bf18-9cbd-4edc-b340-424beb43209a","shortId":"arSUc5","kind":"skill","title":"typescript-effect-ts","tagline":"Type-safe functional effects with Effect-TS. Use when building applications with Effect, using Effect.gen generators, handling typed errors, managing services with Layer and Context.Tag, validating data with Schema, or managing resources with acquireRelease.","description":"# Effect-TS\n\nEffect is a powerful TypeScript library for building robust, type-safe applications. It provides a functional effect system with typed errors, dependency injection, resource management, and concurrency primitives.\n\n## Why Effect?\n\n**Type-safe errors**: Unlike Promise which loses error type information, Effect tracks errors in the type system with `Effect<Success, Error, Requirements>`.\n\n**Dependency injection**: Services are declared explicitly using `Context.Tag` and provided via `Layer`, making dependencies visible in types.\n\n**Resource safety**: `Effect.acquireRelease` ensures resources are always cleaned up, even on failure or interruption.\n\n**Composability**: Effects compose naturally with `pipe`, `Effect.gen` generators, and combinators like `Effect.all`, `Effect.flatMap`.\n\n## Quick Reference\n\nFor detailed patterns and examples, see:\n- [Core Concepts](./references/core.md) - Effect type, creating and running effects, generators\n- [Error Handling](./references/error-handling.md) - Typed errors, catchAll, catchTag, Either\n- [Services & Layers](./references/services.md) - Dependency injection with Context.Tag and Layer\n- [Schema](./references/schema.md) - Data validation with encode/decode\n- [Resources](./references/resources.md) - Resource management with acquireRelease and Scope\n\n## The Effect Type\n\nThe core type `Effect<Success, Error, Requirements>` represents a computation that:\n- Produces a value of type `Success` on success\n- May fail with an error of type `Error`\n- Requires a context of type `Requirements` to run\n\n```typescript\nimport { Effect } from \"effect\"\n\n// Effect<number, never, never> - succeeds with number, cannot fail, no requirements\nconst succeed = Effect.succeed(42)\n\n// Effect<never, string, never> - always fails with string error\nconst fail = Effect.fail(\"Something went wrong\")\n\n// Effect<string, Error, never> - may succeed with string or fail with Error\nconst parse = (input: string): Effect.Effect<number, Error> =>\n  Effect.try({\n    try: () => JSON.parse(input),\n    catch: (e) => new Error(`Parse failed: ${e}`)\n  })\n```\n\n## Creating Effects\n\n```typescript\nimport { Effect } from \"effect\"\n\n// From synchronous values\nconst fromValue = Effect.succeed(42)\nconst fromThunk = Effect.sync(() => Date.now())\n\n// From failures\nconst fromError = Effect.fail(new Error(\"Failed\"))\nconst fromDie = Effect.die(\"Unexpected error\") // Defect, not typed\n\n// From async operations\nconst fromPromise = Effect.tryPromise({\n  try: () => fetch(\"/api/data\").then(r => r.json()),\n  catch: (e) => new Error(`Fetch failed: ${e}`)\n})\n\n// From nullable values\nconst fromNullable = Effect.fromNullable(maybeValue)\n```\n\n## Running Effects\n\n```typescript\nimport { Effect } from \"effect\"\n\nconst program = Effect.succeed(42)\n\n// Synchronous (throws if effect is async or fails)\nconst result = Effect.runSync(program) // 42\n\n// Promise-based\nconst promise = Effect.runPromise(program) // Promise<42>\n\n// With exit status\nconst exit = Effect.runSyncExit(program)\nconst exitPromise = Effect.runPromiseExit(program)\n```\n\n## Effect.gen Generators\n\nWrite async-looking code with full type safety using generators:\n\n```typescript\nimport { Effect } from \"effect\"\n\nconst program = Effect.gen(function* () {\n  const user = yield* fetchUser(userId)\n  const posts = yield* fetchPosts(user.id)\n  const enriched = yield* enrichPosts(posts)\n  return { user, posts: enriched }\n})\n\n// Equivalent to:\nconst programPipe = fetchUser(userId).pipe(\n  Effect.flatMap(user =>\n    fetchPosts(user.id).pipe(\n      Effect.flatMap(posts =>\n        enrichPosts(posts).pipe(\n          Effect.map(enriched => ({ user, posts: enriched }))\n        )\n      )\n    )\n  )\n)\n```\n\n## Error Handling\n\nEffect tracks errors in the type system, making error handling explicit:\n\n```typescript\nimport { Effect } from \"effect\"\n\nclass UserNotFound extends Error {\n  readonly _tag = \"UserNotFound\"\n  constructor(readonly userId: string) {\n    super(`User not found: ${userId}`)\n  }\n}\n\nclass DatabaseError extends Error {\n  readonly _tag = \"DatabaseError\"\n  constructor(readonly cause: unknown) {\n    super(\"Database error\")\n  }\n}\n\n// Effect<User, UserNotFound | DatabaseError, never>\nconst getUser = (id: string) => Effect.gen(function* () {\n  const record = yield* queryDatabase(id)\n  if (!record) {\n    return yield* Effect.fail(new UserNotFound(id))\n  }\n  return record\n})\n\n// Handle specific errors\nconst handled = getUser(\"123\").pipe(\n  Effect.catchTag(\"UserNotFound\", (e) =>\n    Effect.succeed({ id: e.userId, name: \"Anonymous\" })\n  )\n)\n\n// Handle all errors\nconst recovered = getUser(\"123\").pipe(\n  Effect.catchAll((error) =>\n    Effect.succeed({ id: \"unknown\", name: \"Fallback\" })\n  )\n)\n```\n\n## Services and Dependency Injection\n\nDefine services with `Context.Tag` and provide them with `Layer`:\n\n```typescript\nimport { Context, Effect, Layer } from \"effect\"\n\n// Define a service interface and tag\nclass Database extends Context.Tag(\"Database\")<\n  Database,\n  {\n    readonly query: (sql: string) => Effect.Effect<unknown[]>\n    readonly execute: (sql: string) => Effect.Effect<void>\n  }\n>() {}\n\n// Use the service in effects\nconst getUsers = Effect.gen(function* () {\n  const db = yield* Database\n  return yield* db.query(\"SELECT * FROM users\")\n})\n\n// Create a layer that provides the service\nconst DatabaseLive = Layer.succeed(Database, {\n  query: (sql) => Effect.sync(() => {\n    console.log(`Executing: ${sql}`)\n    return []\n  }),\n  execute: (sql) => Effect.sync(() => {\n    console.log(`Executing: ${sql}`)\n  })\n})\n\n// Provide the layer to run the effect\nconst runnable = Effect.provide(getUsers, DatabaseLive)\nEffect.runPromise(runnable)\n```\n\n## Schema Validation\n\nUse Effect Schema for type-safe data validation:\n\n```typescript\nimport { Schema } from \"effect\"\n\n// Define a schema\nconst User = Schema.Struct({\n  id: Schema.String,\n  name: Schema.String,\n  email: Schema.String,\n  age: Schema.Number\n})\n\n// Infer types from schema\ntype User = Schema.Schema.Type<typeof User>\n\n// Decode unknown data\nconst decoded = Schema.decodeUnknownSync(User)({\n  id: \"123\",\n  name: \"Alice\",\n  email: \"alice@example.com\",\n  age: 30\n})\n\n// Decode with Effect (for better error handling)\nconst decodeEffect = Schema.decodeUnknown(User)\nconst result = decodeEffect({ id: \"123\", name: \"Alice\", email: \"a@b.com\", age: 30 })\n// Effect<User, ParseError, never>\n```\n\n## Resource Management\n\nSafely manage resources that need cleanup:\n\n```typescript\nimport { Effect, Scope } from \"effect\"\n\n// Define a resource with acquisition and release\nconst withConnection = Effect.acquireRelease(\n  // Acquire\n  Effect.sync(() => {\n    console.log(\"Opening connection\")\n    return { query: (sql: string) => sql }\n  }),\n  // Release\n  (conn) => Effect.sync(() => {\n    console.log(\"Closing connection\")\n  })\n)\n\n// Use the resource in a scoped effect\nconst program = Effect.scoped(\n  Effect.gen(function* () {\n    const conn = yield* withConnection\n    return conn.query(\"SELECT * FROM users\")\n  })\n)\n\n// Connection is automatically closed after use\nEffect.runPromise(program)\n```\n\n## Common Patterns\n\n### Sequential vs Parallel Execution\n\n```typescript\nimport { Effect } from \"effect\"\n\nconst tasks = [task1, task2, task3]\n\n// Sequential execution\nconst sequential = Effect.all(tasks, { concurrency: 1 })\n\n// Parallel execution (all at once)\nconst parallel = Effect.all(tasks, { concurrency: \"unbounded\" })\n\n// Parallel with limit\nconst limited = Effect.all(tasks, { concurrency: 5 })\n```\n\n### Retry with Backoff\n\n```typescript\nimport { Effect, Schedule } from \"effect\"\n\nconst retried = fetchData.pipe(\n  Effect.retry(\n    Schedule.exponential(\"100 millis\").pipe(\n      Schedule.jittered,\n      Schedule.compose(Schedule.recurs(5))\n    )\n  )\n)\n```\n\n### Timeouts\n\n```typescript\nimport { Effect, Duration } from \"effect\"\n\nconst withTimeout = longRunningTask.pipe(\n  Effect.timeout(Duration.seconds(30))\n)\n```\n\n## Guidelines\n\n1. **Use `Effect.gen` for complex flows** - Generators make sequential operations readable\n2. **Tag errors with `_tag`** - Enables `catchTag` for precise error handling\n3. **Define services with `Context.Tag`** - Makes dependencies explicit and testable\n4. **Use `Layer` for service composition** - Layers compose and manage lifecycle\n5. **Prefer `Effect.try` over manual try/catch** - Keeps errors in the Effect channel\n6. **Use Schema for external data** - Validates and transforms API/DB data\n7. **Scope resources with `acquireRelease`** - Guarantees cleanup on success or failure\n8. **Run effects at the edge** - Keep `runPromise`/`runSync` at application boundaries\n\n## Integration with Other Skills\n\nEffect-TS integrates well with:\n- [Functional Patterns](../functional-patterns/SKILL.md) - Effect builds on Option/Either concepts\n- [Drizzle ORM](../drizzle-orm/SKILL.md) - Wrap Drizzle queries in Effect for typed errors\n- [Fastify](../fastify/SKILL.md) - Use Effect for request handling with typed errors\n\n## When This Skill Loads\n\nThis skill automatically loads when discussing:\n- Effect-TS library usage\n- Typed error handling in TypeScript\n- Dependency injection with Context and Layer\n- Effect.gen generators\n- Schema validation with Effect\n- Resource management and Scope\n- Functional effect systems","tags":["typescript","effect","atelier","martinffx","agent-skills","agentic-coding","anthropic","claude-code","claude-skills","code-review","codex","codex-skill"],"capabilities":["skill","source-martinffx","skill-typescript-effect-ts","topic-agent-skills","topic-agentic-coding","topic-anthropic","topic-claude-code","topic-claude-skills","topic-code-review","topic-codex","topic-codex-skill","topic-opencode","topic-prompt-engineering","topic-sdd","topic-spec-driven-development"],"categories":["atelier"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/martinffx/atelier/typescript-effect-ts","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add martinffx/atelier","source_repo":"https://github.com/martinffx/atelier","install_from":"skills.sh"}},"qualityScore":"0.461","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 23 github stars · SKILL.md body (9,207 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:05:25.115Z","embedding":null,"createdAt":"2026-05-10T07:03:13.615Z","updatedAt":"2026-05-18T19:05:25.115Z","lastSeenAt":"2026-05-18T19:05:25.115Z","tsv":"'/api/data':336 '/drizzle-orm/skill.md':1001 '/fastify/skill.md':1011 '/functional-patterns/skill.md':993 '/references/core.md':152 '/references/error-handling.md':162 '/references/resources.md':184 '/references/schema.md':178 '/references/services.md':170 '1':836,892 '100':871 '123':541,557,711,733 '2':903 '3':914 '30':717,739,890 '4':924 '42':248,307,364,377,386 '5':856,877,935 '6':947 '7':958 '8':969 'a@b.com':737 'acquir':768 'acquirereleas':40,188,962 'acquisit':762 'age':694,716,738 'alic':713,735 'alice@example.com':715 'alway':121,253 'anonym':550 'api/db':956 'applic':17,56,979 'async':329,370,402 'async-look':401 'automat':807,1026 'backoff':859 'base':380 'better':722 'boundari':980 'build':16,51,995 'cannot':241 'catch':287,340 'catchal':165 'catchtag':166,909 'caus':504 'channel':946 'class':479,495,592 'clean':122 'cleanup':751,964 'close':782,808 'code':404 'combin':138 'common':813 'complex':896 'compos':129,131,931 'composit':929 'comput':203 'concept':151,998 'concurr':71,835,846,855 'conn':779,797 'conn.query':801 'connect':772,783,805 'console.log':642,649,770,781 'const':245,258,276,304,308,314,320,331,350,361,373,381,390,394,416,420,425,430,441,514,520,538,554,614,618,635,659,685,706,725,729,765,791,796,824,831,842,851,866,885 'constructor':486,502 'context':223,581,1043 'context.tag':31,105,174,573,595,918 'core':150,195 'creat':155,294,628 'data':33,179,675,705,952,957 'databas':507,593,596,597,621,638 'databaseerror':496,501,512 'databasel':636,663 'date.now':311 'db':619 'db.query':624 'declar':102 'decod':703,707,718 'decodeeffect':726,731 'defect':325 'defin':570,586,682,758,915 'depend':66,98,111,171,568,920,1040 'detail':145 'discuss':1029 'drizzl':999,1003 'durat':882 'duration.seconds':889 'e':288,293,341,346,545 'e.userid':548 'edg':974 'effect':3,9,12,19,42,44,61,74,86,94,130,153,158,192,197,231,233,234,249,264,295,298,300,355,358,360,368,413,415,463,476,478,509,582,585,613,658,669,681,720,740,754,757,790,821,823,862,865,881,884,945,971,986,994,1006,1013,1031,1051,1057 'effect-t':11,41,985,1030 'effect.acquirerelease':117,767 'effect.all':140,833,844,853 'effect.catchall':559 'effect.catchtag':543 'effect.die':322 'effect.effect':280,602,608 'effect.fail':260,316,529 'effect.flatmap':141,446,451 'effect.fromnullable':352 'effect.gen':21,135,398,418,518,616,794,894,1046 'effect.map':456 'effect.provide':661 'effect.retry':869 'effect.runpromise':383,664,811 'effect.runpromiseexit':396 'effect.runsync':375 'effect.runsyncexit':392 'effect.scoped':793 'effect.succeed':247,306,363,546,561 'effect.sync':310,641,648,769,780 'effect.timeout':888 'effect.try':283,937 'effect.trypromise':333 'either':167 'email':692,714,736 'enabl':908 'encode/decode':182 'enrich':431,438,457,460 'enrichpost':433,453 'ensur':118 'equival':439 'error':25,65,78,83,88,96,160,164,199,217,220,257,266,275,282,290,318,324,343,461,465,471,482,498,508,537,553,560,723,905,912,942,1009,1019,1036 'even':124 'exampl':148 'execut':605,643,646,650,818,830,838 'exit':388,391 'exitpromis':395 'explicit':103,473,921 'extend':481,497,594 'extern':951 'fail':214,242,254,259,273,292,319,345,372 'failur':126,313,968 'fallback':565 'fastifi':1010 'fetch':335,344 'fetchdata.pipe':868 'fetchpost':428,448 'fetchus':423,443 'flow':897 'found':493 'fromdi':321 'fromerror':315 'fromnul':351 'frompromis':332 'fromthunk':309 'fromvalu':305 'full':406 'function':8,60,419,519,617,795,991,1056 'generat':22,136,159,399,410,898,1047 'getus':515,540,556,615,662 'guarante':963 'guidelin':891 'handl':23,161,462,472,535,539,551,724,913,1016,1037 'id':516,524,532,547,562,688,710,732 'import':230,297,357,412,475,580,678,753,820,861,880 'infer':696 'inform':85 'inject':67,99,172,569,1041 'input':278,286 'integr':981,988 'interfac':589 'interrupt':128 'json.parse':285 'keep':941,975 'layer':29,109,169,176,578,583,630,654,926,930,1045 'layer.succeed':637 'librari':49,1033 'lifecycl':934 'like':139 'limit':850,852 'load':1023,1027 'longrunningtask.pipe':887 'look':403 'lose':82 'make':110,470,899,919 'manag':26,37,69,186,745,747,933,1053 'manual':939 'may':213,268 'maybevalu':353 'milli':872 'name':549,564,690,712,734 'natur':132 'need':750 'never':236,237,250,252,267,513,743 'new':289,317,342,530 'nullabl':348 'number':235,240,281 'open':771 'oper':330,901 'option/either':997 'orm':1000 'parallel':817,837,843,848 'pars':277,291 'parseerror':742 'pattern':146,814,992 'pipe':134,445,450,455,542,558,873 'post':426,434,437,452,454,459 'power':47 'precis':911 'prefer':936 'primit':72 'produc':205 'program':362,376,384,393,397,417,792,812 'programpip':442 'promis':80,379,382,385 'promise-bas':378 'provid':58,107,575,632,652 'queri':599,639,774,1004 'querydatabas':523 'quick':142 'r':338 'r.json':339 'readabl':902 'readon':483,487,499,503,598,604 'record':521,526,534 'recov':555 'refer':143 'releas':764,778 'repres':201 'request':1015 'requir':97,200,221,226,244 'resourc':38,68,115,119,183,185,744,748,760,786,960,1052 'result':374,730 'retri':857,867 'return':435,527,533,622,645,773,800 'robust':52 'run':157,228,354,656,970 'runnabl':660,665 'runpromis':976 'runsync':977 'safe':7,55,77,674,746 'safeti':116,408 'schedul':863 'schedule.compose':875 'schedule.exponential':870 'schedule.jittered':874 'schedule.recurs':876 'schema':35,177,666,670,679,684,699,949,1048 'schema.decodeunknown':727 'schema.decodeunknownsync':708 'schema.number':695 'schema.schema.type':702 'schema.string':689,691,693 'schema.struct':687 'scope':190,755,789,959,1055 'see':149 'select':625,802 'sequenti':815,829,832,900 'servic':27,100,168,566,571,588,611,634,916,928 'skill':984,1022,1025 'skill-typescript-effect-ts' 'someth':261 'source-martinffx' 'specif':536 'sql':600,606,640,644,647,651,775,777 'status':389 'string':251,256,265,271,279,489,517,601,607,776 'succeed':238,246,269 'success':95,198,210,212,966 'super':490,506 'synchron':302,365 'system':62,92,469,1058 'tag':484,500,591,904,907 'task':825,834,845,854 'task1':826 'task2':827 'task3':828 'testabl':923 'throw':366 'timeout':878 'topic-agent-skills' 'topic-agentic-coding' 'topic-anthropic' 'topic-claude-code' 'topic-claude-skills' 'topic-code-review' 'topic-codex' 'topic-codex-skill' 'topic-opencode' 'topic-prompt-engineering' 'topic-sdd' 'topic-spec-driven-development' 'track':87,464 'transform':955 'tri':284,334 'try/catch':940 'ts':4,13,43,987,1032 'type':6,24,54,64,76,84,91,114,154,163,193,196,209,219,225,327,407,468,673,697,700,1008,1018,1035 'type-saf':5,53,75,672 'typescript':2,48,229,296,356,411,474,579,677,752,819,860,879,1039 'typescript-effect-t':1 'unbound':847 'unexpect':323 'unknown':505,563,603,704 'unlik':79 'usag':1034 'use':14,20,104,409,609,668,784,810,893,925,948,1012 'user':421,436,447,458,491,510,627,686,701,709,728,741,804 'user.id':429,449 'userid':424,444,488,494 'usernotfound':480,485,511,531,544 'valid':32,180,667,676,953,1049 'valu':207,303,349 'via':108 'visibl':112 'vs':816 'well':989 'went':262 'withconnect':766,799 'withtimeout':886 'wrap':1002 'write':400 'wrong':263 'yield':422,427,432,522,528,620,623,798","prices":[{"id":"f16b44be-d1ca-45c8-8d67-1108b91cd670","listingId":"e280bf18-9cbd-4edc-b340-424beb43209a","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"martinffx","category":"atelier","install_from":"skills.sh"},"createdAt":"2026-05-10T07:03:13.615Z"}],"sources":[{"listingId":"e280bf18-9cbd-4edc-b340-424beb43209a","source":"github","sourceId":"martinffx/atelier/typescript-effect-ts","sourceUrl":"https://github.com/martinffx/atelier/tree/main/skills/typescript-effect-ts","isPrimary":false,"firstSeenAt":"2026-05-10T07:03:13.615Z","lastSeenAt":"2026-05-18T19:05:25.115Z"}],"details":{"listingId":"e280bf18-9cbd-4edc-b340-424beb43209a","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"martinffx","slug":"typescript-effect-ts","github":{"repo":"martinffx/atelier","stars":23,"topics":["agent-skills","agentic-coding","anthropic","claude-code","claude-skills","code-review","codex","codex-skill","opencode","prompt-engineering","sdd","spec-driven-development"],"license":"mit","html_url":"https://github.com/martinffx/atelier","pushed_at":"2026-05-18T06:56:45Z","description":"An atelier for Opencode, Claude Code, and other coding agents: spec-driven workflows, deep thinking, and code quality.","skill_md_sha":"d70f139064c3dcd2e2a048635c4befb54d3211dc","skill_md_path":"skills/typescript-effect-ts/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/martinffx/atelier/tree/main/skills/typescript-effect-ts"},"layout":"multi","source":"github","category":"atelier","frontmatter":{"name":"typescript-effect-ts","description":"Type-safe functional effects with Effect-TS. Use when building applications with Effect, using Effect.gen generators, handling typed errors, managing services with Layer and Context.Tag, validating data with Schema, or managing resources with acquireRelease."},"skills_sh_url":"https://skills.sh/martinffx/atelier/typescript-effect-ts"},"updatedAt":"2026-05-18T19:05:25.115Z"}}