{"id":"7fc45312-3907-4d45-8d77-d732275cb1eb","shortId":"ZspXCr","kind":"skill","title":"typescript-functional-patterns","tagline":"Functional programming patterns for reliable TypeScript. Use when modeling state machines, discriminated unions, Result/Option types, branded types, or building type-safe domain models.","description":"# Functional Patterns for Reliable TypeScript\n\nBuild reliable systems using Algebraic Data Types (ADTs), discriminated unions, Result/Option types, and branded types. These patterns enable the compiler to prove correctness, prevent runtime errors, and make illegal states unrepresentable.\n\n## Why Functional Patterns?\n\n**Reliability through types**: Use the type system to encode business rules, making invalid states impossible to construct. The compiler becomes your safety net, catching errors at build time rather than runtime.\n\n**Key benefits:**\n- Exhaustiveness checking prevents missing cases\n- Impossible states become unrepresentable\n- Business logic encoded in types, not runtime checks\n- Refactoring becomes safe and mechanical\n- Self-documenting code through types\n\n## Quick Reference\n\nFor detailed patterns and examples, see:\n- [ADTs (Algebraic Data Types)](./references/adts.md) - Sum types, product types, discriminated unions\n- [Option & Result](./references/option-result.md) - Type-safe error handling and nullable values\n- [Branded Types](./references/branded-types.md) - Smart constructors and nominal typing\n- [Migration Guide](./references/migration-guide.md) - Step-by-step adoption playbook\n\n## Core Patterns Overview\n\n### 1. Discriminated Unions (Sum Types)\n\nModel \"one of several variants\" with exhaustive pattern matching:\n\n```typescript\ntype PaymentMethod =\n  | { kind: \"card\"; last4: string; brand: string }\n  | { kind: \"ach\"; accountNumber: string; routingNumber: string }\n  | { kind: \"wallet\"; provider: \"apple\" | \"google\" }\n\nfunction processPayment(method: PaymentMethod): void {\n  switch (method.kind) {\n    case \"card\":\n      // TypeScript knows: method.last4 and method.brand exist\n      return processCard(method.last4, method.brand)\n    case \"ach\":\n      // TypeScript knows: method.accountNumber and method.routingNumber exist\n      return processACH(method.accountNumber, method.routingNumber)\n    case \"wallet\":\n      // TypeScript knows: method.provider exists\n      return processWallet(method.provider)\n    default:\n      assertNever(method) // Compiler error if cases missing\n  }\n}\n```\n\n### 2. Option Type (Nullable Values)\n\nExplicit handling of \"value may be absent\":\n\n```typescript\ntype Option<T> = { _tag: \"None\" } | { _tag: \"Some\"; value: T }\n\nfunction findUser(id: string): Option<User> {\n  const user = database.get(id)\n  return user ? Some(user) : None\n}\n\nconst result = findUser(\"123\")\nswitch (result._tag) {\n  case \"Some\":\n    console.log(result.value.name) // Type-safe access\n    break\n  case \"None\":\n    console.log(\"User not found\")\n    break\n}\n```\n\n### 3. Result Type (Error Handling)\n\nExplicit error handling without exceptions:\n\n```typescript\ntype Result<T, E> = { _tag: \"Ok\"; value: T } | { _tag: \"Err\"; error: E }\n\nfunction parseConfig(raw: string): Result<Config, ParseError> {\n  try {\n    const data = JSON.parse(raw)\n    return Ok(validateConfig(data))\n  } catch (e) {\n    return Err({ message: \"Invalid JSON\", cause: e })\n  }\n}\n\nconst result = parseConfig(rawConfig)\nswitch (result._tag) {\n  case \"Ok\":\n    startServer(result.value)\n    break\n  case \"Err\":\n    logger.error(result.error.message)\n    break\n}\n```\n\n### 4. Branded Types (Type-Safe Units)\n\nPrevent unit confusion and invalid values:\n\n```typescript\ntype Brand<K, T> = K & { __brand: T }\ntype Cents = Brand<number, \"Cents\">\ntype Dollars = Brand<number, \"Dollars\">\n\nconst Cents = (n: number): Cents => {\n  if (!Number.isInteger(n) || n < 0) throw new Error(\"Invalid cents\")\n  return n as Cents\n}\n\nconst Dollars = (n: number): Dollars => {\n  if (n < 0) throw new Error(\"Invalid dollars\")\n  return n as Dollars\n}\n\n// Compiler prevents mixing units:\nconst price: Cents = Cents(100)\nconst budget: Dollars = Dollars(10)\nconst total: Cents = price + budget // Type error! Cannot mix Cents and Dollars\n```\n\n## When to Use\n\n### Use Discriminated Unions When:\n- Modeling state machines (pending → settled → reconciled)\n- Representing mutually exclusive variants (payment methods, user roles)\n- Building domain models with distinct states\n- Replacing boolean flags with explicit states\n\n### Use Option When:\n- Value may be absent (but absence is expected/valid)\n- Replacing `null` or `undefined` checks\n- Chaining operations that may fail to find values\n- Making nullability explicit in APIs\n\n### Use Result When:\n- Operation may fail with recoverable errors\n- You need to propagate error context\n- Replacing try/catch for expected failures\n- Building error handling into function signatures\n\n### Use Branded Types When:\n- Preventing unit confusion (cents vs dollars, ms vs seconds)\n- Enforcing validation invariants (email format, positive numbers)\n- Creating type-safe IDs (UserId vs OrderId)\n- Domain-driven design with value objects\n\n## Quick Start - Paste-Ready Helpers\n\nCopy these into your project to start using functional patterns:\n\n```typescript\n// ============================================\n// Option Type\n// ============================================\ntype None = { _tag: \"None\" }\ntype Some<T> = { _tag: \"Some\"; value: T }\ntype Option<T> = None | Some<T>\n\nconst None: None = { _tag: \"None\" }\nconst Some = <T>(value: T): Option<T> => ({ _tag: \"Some\", value })\n\n// Utilities\nconst isNone = <T>(opt: Option<T>): opt is None => opt._tag === \"None\"\nconst isSome = <T>(opt: Option<T>): opt is Some<T> => opt._tag === \"Some\"\n\nconst getOrElse = <T>(opt: Option<T>, defaultValue: T): T =>\n  opt._tag === \"Some\" ? opt.value : defaultValue\n\nconst map = <T, U>(opt: Option<T>, fn: (value: T) => U): Option<U> =>\n  opt._tag === \"Some\" ? Some(fn(opt.value)) : None\n\nconst flatMap = <T, U>(opt: Option<T>, fn: (value: T) => Option<U>): Option<U> =>\n  opt._tag === \"Some\" ? fn(opt.value) : None\n\n// ============================================\n// Result Type\n// ============================================\ntype Ok<T> = { _tag: \"Ok\"; value: T }\ntype Err<E> = { _tag: \"Err\"; error: E }\ntype Result<T, E> = Ok<T> | Err<E>\n\nconst Ok = <T>(value: T): Result<T, never> => ({ _tag: \"Ok\", value })\nconst Err = <E>(error: E): Result<never, E> => ({ _tag: \"Err\", error })\n\n// Utilities\nconst isOk = <T, E>(result: Result<T, E>): result is Ok<T> => result._tag === \"Ok\"\nconst isErr = <T, E>(result: Result<T, E>): result is Err<E> => result._tag === \"Err\"\n\nconst mapResult = <T, U, E>(result: Result<T, E>, fn: (value: T) => U): Result<U, E> =>\n  result._tag === \"Ok\" ? Ok(fn(result.value)) : result\n\nconst flatMapResult = <T, U, E>(\n  result: Result<T, E>,\n  fn: (value: T) => Result<U, E>\n): Result<U, E> =>\n  result._tag === \"Ok\" ? fn(result.value) : result\n\n// ============================================\n// Exhaustiveness Checking\n// ============================================\nconst assertNever = (x: never): never => {\n  throw new Error(`Unhandled variant: ${JSON.stringify(x)}`)\n}\n\n// ============================================\n// Branded Types\n// ============================================\ntype Brand<K, T> = K & { __brand: T }\n\n// Example: Cents (integer cents to prevent floating point errors)\ntype Cents = Brand<number, \"Cents\">\nconst Cents = (n: number): Cents => {\n  if (!Number.isInteger(n)) throw new Error(\"Cents must be integer\")\n  if (n < 0) throw new Error(\"Cents cannot be negative\")\n  return n as Cents\n}\n\n// Example: Email (validated email address)\ntype Email = Brand<string, \"Email\">\nconst Email = (s: string): Email => {\n  if (!/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(s)) throw new Error(\"Invalid email\")\n  return s as Email\n}\n\n// Example: Millis (timestamp in milliseconds)\ntype Millis = Brand<number, \"Millis\">\nconst Millis = (n: number): Millis => {\n  if (n < 0) throw new Error(\"Millis cannot be negative\")\n  return n as Millis\n}\n```\n\n## Guidelines\n\n### Pattern Matching Best Practices\n\n1. **Always use `assertNever` in default case** for exhaustiveness checking:\n   ```typescript\n   switch (variant.kind) {\n     case \"a\": return handleA(variant)\n     case \"b\": return handleB(variant)\n     default: assertNever(variant) // Compiler error if cases missing\n   }\n   ```\n\n2. **Use discriminant field consistently** (`kind`, `type`, `_tag`):\n   ```typescript\n   // Good: consistent discriminant\n   type Result<T, E> = { _tag: \"Ok\"; value: T } | { _tag: \"Err\"; error: E }\n\n   // Avoid: mixing discriminants\n   type Bad = { kind: \"a\" } | { type: \"b\" } // Inconsistent!\n   ```\n\n3. **Narrow types early** to unlock type safety:\n   ```typescript\n   if (result._tag === \"Ok\") {\n     // TypeScript knows: result.value exists\n     return result.value.data\n   }\n   ```\n\n### Error Handling Strategy\n\n1. **Use Option for expected absence**:\n   ```typescript\n   function findUser(id: string): Option<User>\n   ```\n\n2. **Use Result for recoverable errors**:\n   ```typescript\n   function parseConfig(raw: string): Result<Config, ParseError>\n   ```\n\n3. **Use exceptions for programmer errors**:\n   ```typescript\n   function unreachable(message: string): never {\n     throw new Error(`Unreachable: ${message}`)\n   }\n   ```\n\n### Branded Types Guidelines\n\n1. **Validate in smart constructor**:\n   ```typescript\n   const PositiveInt = (n: number): PositiveInt => {\n     if (!Number.isInteger(n) || n <= 0) throw new Error(\"Must be positive integer\")\n     return n as PositiveInt\n   }\n   ```\n\n2. **Use branded types for domain concepts**:\n   ```typescript\n   type UserId = Brand<string, \"UserId\">\n   type OrderId = Brand<string, \"OrderId\">\n   // Compiler prevents: const userId: UserId = orderId\n   ```\n\n3. **Prevent unit confusion**:\n   ```typescript\n   type Seconds = Brand<number, \"Seconds\">\n   type Millis = Brand<number, \"Millis\">\n   // Compiler prevents: const s: Seconds = millis\n   ```\n\n### Migration Strategy\n\nStart small and expand:\n1. New features: Use functional patterns from day one\n2. Bug fixes: Refactor to discriminated unions when touching code\n3. High-risk areas: Prioritize financial calculations, state machines\n4. Team adoption: Share paste-ready helpers, pair on first implementations\n\nEnable TypeScript strict mode flags:\n- `strictNullChecks: true` - Make nullability explicit\n- `noImplicitReturns: true` - Ensure all code paths return\n- `strictFunctionTypes: true` - Safer function signatures\n\n## Examples by Domain\n\n### State Machine (Transaction Lifecycle)\n```typescript\ntype TxnState =\n  | { kind: \"pending\"; createdAt: Millis }\n  | { kind: \"settled\"; ledgerId: string; settledAt: Millis }\n  | { kind: \"failed\"; reason: FailureReason; failedAt: Millis }\n  | { kind: \"reversed\"; originalLedgerId: string; reversedAt: Millis }\n\nfunction canReverse(state: TxnState): boolean {\n  switch (state.kind) {\n    case \"pending\": return false\n    case \"settled\": return true\n    case \"failed\": return false\n    case \"reversed\": return false\n    default: assertNever(state)\n  }\n}\n```\n\n### Configuration Parsing\n```typescript\ntype ConfigError = { field: string; message: string }\n\nfunction parsePort(raw: unknown): Result<number, ConfigError> {\n  if (typeof raw !== \"number\") {\n    return Err({ field: \"port\", message: \"must be number\" })\n  }\n  if (raw < 1 || raw > 65535) {\n    return Err({ field: \"port\", message: \"must be 1-65535\" })\n  }\n  return Ok(raw)\n}\n```\n\n### Financial Calculations\n```typescript\ntype Cents = Brand<number, \"Cents\">\n\nfunction addCents(a: Cents, b: Cents): Cents {\n  return Cents(a + b) // Smart constructor validates result\n}\n\nfunction calculateFee(amount: Cents, bps: number): Cents {\n  const feeAmount = Math.round((amount * bps) / 10000)\n  return Cents(feeAmount)\n}\n```\n\n## Further Reading\n\n- [ADT Reference](./references/adts.md) - Deep dive on sum types, product types, and pattern matching\n- [Option & Result Reference](./references/option-result.md) - Comprehensive error handling patterns\n- [Branded Types Reference](./references/branded-types.md) - Advanced nominal typing techniques\n- [Migration Guide](./references/migration-guide.md) - Step-by-step adoption playbook\n\n## Credits\n\nThese patterns are inspired by **[Why Reliability Demands Functional Programming, ADTs, Safety and Critical Infrastructure](https://rastrian.com/why-reliability-demands-functional-programming-adts-safety-and-critical-infrastructure/)** by Rastrian. The blog post explores how functional programming techniques and Algebraic Data Types enable building reliable systems in critical infrastructure contexts.\n\n## When This Skill Loads\n\nThis skill automatically loads when discussing:\n- Discriminated unions and sum types\n- State machine modeling\n- Result/Option types and error handling\n- Branded types and smart constructors\n- Type-safe domain models\n- Making illegal states unrepresentable\n- Functional programming in TypeScript","tags":["typescript","functional","patterns","atelier","martinffx","agent-skills","agentic-coding","anthropic","claude-code","claude-skills","code-review","codex"],"capabilities":["skill","source-martinffx","skill-typescript-functional-patterns","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-functional-patterns","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 (12,567 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.340Z","embedding":null,"createdAt":"2026-05-10T07:03:13.787Z","updatedAt":"2026-05-18T19:05:25.340Z","lastSeenAt":"2026-05-18T19:05:25.340Z","tsv":"'-65535':1321 '/.test':904 '/references/adts.md':141,1368 '/references/branded-types.md':161,1390 '/references/migration-guide.md':169,1397 '/references/option-result.md':150,1382 '/why-reliability-demands-functional-programming-adts-safety-and-critical-infrastructure/)**':1422 '0':422,439,873,932,1096 '1':179,949,1035,1081,1159,1310,1320 '10':462 '100':457 '10000':1360 '123':299 '2':261,980,1047,1108,1168 '3':318,1014,1061,1132,1178 '4':382,1188 '65535':1312 'absenc':516,1040 'absent':272,514 'access':309 'accountnumb':204 'ach':203,233 'addcent':1334 'address':889 'adopt':174,1190,1402 'adt':41,137,1366,1415 'advanc':1391 'algebra':38,138,1434 'alway':950 'amount':1350,1358 'api':536 'appl':211 'area':1182 'assertnev':254,822,952,973,1278 'automat':1451 'avoid':1004 'b':968,1012,1337,1343 'bad':1008 'becom':87,108,119 'benefit':100 'best':947 'blog':1426 'boolean':503,1258 'bps':1352,1359 'brand':20,47,159,200,383,397,401,405,410,564,833,836,840,853,892,922,1078,1110,1118,1123,1139,1144,1330,1387,1468 'break':310,317,376,381 'budget':459,467 'bug':1169 'build':23,34,94,496,557,1438 'busi':77,110 'calcul':1185,1326 'calculatefe':1349 'cannot':470,878,937 'canrevers':1255 'card':197,221 'case':105,220,232,244,259,302,311,372,377,955,962,967,978,1261,1265,1269,1273 'catch':91,357 'caus':364 'cent':404,407,414,417,427,431,455,456,465,472,570,843,845,852,855,857,860,867,877,884,1329,1332,1336,1338,1339,1341,1351,1354,1362 'chain':524 'check':102,117,523,820,958 'code':126,1177,1214 'compil':53,86,256,449,975,1126,1147 'comprehens':1383 'concept':1114 'config':346,1059 'configerror':1284,1295 'configur':1280 'confus':391,569,1135 'consist':984,990 'console.log':304,313 'const':287,296,349,366,413,432,453,458,463,631,636,645,654,663,674,691,727,737,748,761,774,796,821,856,895,925,1087,1128,1149,1355 'construct':84 'constructor':163,1085,1345,1472 'context':551,1444 'copi':604 'core':176 'correct':56 'creat':583 'createdat':1234 'credit':1404 'critic':1418,1442 'data':39,139,350,356,1435 'database.get':289 'day':1166 'deep':1369 'default':253,954,972,1277 'defaultvalu':667,673 'demand':1412 'design':594 'detail':132 'discrimin':16,42,146,180,479,982,991,1006,1173,1455 'discuss':1454 'distinct':500 'dive':1370 'document':125 'dollar':409,412,433,436,444,448,460,461,474,572 'domain':27,497,592,1113,1224,1476 'domain-driven':591 'driven':593 'e':332,340,358,365,720,724,740,743,751,755,764,768,778,782,789,800,804,810,813,995,1003 'earli':1017 'email':579,886,888,891,894,896,899,910,914 'enabl':51,1200,1437 'encod':76,112 'enforc':576 'ensur':1212 'err':338,360,378,716,718,726,738,745,771,773,1001,1301,1314 'error':59,92,154,257,321,324,339,425,442,469,545,550,558,719,739,746,828,850,866,876,908,935,976,1002,1032,1052,1066,1075,1099,1384,1466 'exampl':135,842,885,915,1222 'except':327,1063 'exclus':490 'exhaust':101,190,819,957 'exist':227,239,249,1029 'expand':1158 'expect':555,1039 'expected/valid':518 'explicit':266,323,506,534,1209 'explor':1428 'fail':528,542,1243,1270 'failedat':1246 'failur':556 'failurereason':1245 'fals':1264,1272,1276 'featur':1161 'feeamount':1356,1363 'field':983,1285,1302,1315 'financi':1184,1325 'find':530 'findus':283,298,1043 'first':1198 'fix':1170 'flag':504,1204 'flatmap':692 'flatmapresult':797 'float':848 'fn':680,688,697,704,783,793,805,816 'format':580 'found':316 'function':3,5,29,66,213,282,341,561,612,1042,1054,1068,1163,1220,1254,1289,1333,1348,1413,1430,1482 'getorels':664 'good':989 'googl':212 'guid':168,1396 'guidelin':944,1080 'handl':155,267,322,325,559,1033,1385,1467 'handlea':965 'handleb':970 'helper':603,1195 'high':1180 'high-risk':1179 'id':284,290,587,1044 'illeg':62,1479 'implement':1199 'imposs':82,106 'inconsist':1013 'infrastructur':1419,1443 'inspir':1408 'integ':844,870,1103 'invalid':80,362,393,426,443,909 'invari':578 'iserr':762 'isnon':646 'isok':749 'issom':655 'json':363 'json.parse':351 'json.stringify':831 'k':398,400,837,839 'key':99 'kind':196,202,208,985,1009,1232,1236,1242,1248 'know':223,235,247,1027 'last4':198 'ledgerid':1238 'lifecycl':1228 'load':1448,1452 'logger.error':379 'logic':111 'machin':15,484,1187,1226,1461 'make':61,79,532,1207,1478 'map':675 'mapresult':775 'match':192,946,1378 'math.round':1357 'may':270,512,527,541 'mechan':122 'messag':361,1070,1077,1287,1304,1317 'method':215,255,493 'method.accountnumber':236,242 'method.brand':226,231 'method.kind':219 'method.last4':224,230 'method.provider':248,252 'method.routingnumber':238,243 'migrat':167,1153,1395 'milli':916,921,924,926,929,936,943,1143,1146,1152,1235,1241,1247,1253 'millisecond':919 'miss':104,260,979 'mix':451,471,1005 'mode':1203 'model':13,28,184,482,498,1462,1477 'ms':573 'must':868,1100,1305,1318 'mutual':489 'n':415,420,421,429,434,438,446,858,863,872,882,927,931,941,1089,1094,1095,1105 'narrow':1015 'need':547 'negat':880,939 'net':90 'never':733,742,824,825,1072 'new':424,441,827,865,875,907,934,1074,1098,1160 'noimplicitreturn':1210 'nomin':165,1392 'none':277,295,312,618,620,629,632,633,635,651,653,690,706 'null':520 'nullabl':157,264,533,1208 'number':406,411,416,435,582,854,859,923,928,1090,1140,1145,1294,1299,1307,1331,1353 'number.isinteger':419,862,1093 'object':597 'ok':334,354,373,710,712,725,728,735,758,760,791,792,815,997,1025,1323 'one':185,1167 'oper':525,540 'opt':647,649,656,658,665,678,695 'opt._tag':652,661,670,685,702 'opt.value':672,689,705 'option':148,262,275,286,509,615,628,640,648,657,666,679,684,696,700,701,1037,1046,1379 'orderid':590,1122,1125,1131 'originalledgerid':1250 'overview':178 'pair':1196 'pars':1281 'parseconfig':342,368,1055 'parseerror':347,1060 'parseport':1290 'past':601,1193 'paste-readi':600,1192 'path':1215 'pattern':4,7,30,50,67,133,177,191,613,945,1164,1377,1386,1406 'payment':492 'paymentmethod':195,216 'pend':485,1233,1262 'playbook':175,1403 'point':849 'port':1303,1316 'posit':581,1102 'positiveint':1088,1091,1107 'post':1427 'practic':948 'prevent':57,103,389,450,567,847,1127,1133,1148 'price':454,466 'priorit':1183 'processach':241 'processcard':229 'processpay':214 'processwallet':251 'product':144,1374 'program':6,1414,1431,1483 'programm':1065 'project':608 'propag':549 'prove':55 'provid':210 'quick':129,598 'rastrian':1424 'rastrian.com':1421 'rastrian.com/why-reliability-demands-functional-programming-adts-safety-and-critical-infrastructure/)**':1420 'rather':96 'raw':343,352,1056,1291,1298,1309,1311,1324 'rawconfig':369 'read':1365 'readi':602,1194 'reason':1244 'reconcil':487 'recover':544,1051 'refactor':118,1171 'refer':130,1367,1381,1389 'reliabl':9,32,35,68,1411,1439 'replac':502,519,552 'repres':488 'result':149,297,319,330,345,367,538,707,722,731,741,752,753,756,765,766,769,779,780,787,795,801,802,808,811,818,993,1049,1058,1293,1347,1380 'result._tag':301,371,759,772,790,814,1024 'result.error.message':380 'result.value':375,794,817,1028 'result.value.data':1031 'result.value.name':305 'result/option':18,44,1463 'return':228,240,250,291,353,359,428,445,881,911,940,964,969,1030,1104,1216,1263,1267,1271,1275,1300,1313,1322,1340,1361 'revers':1249,1274 'reversedat':1252 'risk':1181 'role':495 'routingnumb':206 'rule':78 'runtim':58,98,116 'safe':26,120,153,308,387,586,1475 'safer':1219 'safeti':89,1021,1416 'second':575,1138,1141,1151 'see':136 'self':124 'self-docu':123 'settl':486,1237,1266 'settledat':1240 'sever':187 'share':1191 'signatur':562,1221 'skill':1447,1450 'skill-typescript-functional-patterns' 'small':1156 'smart':162,1084,1344,1471 'source-martinffx' 'start':599,610,1155 'startserv':374 'state':14,63,81,107,483,501,507,1186,1225,1256,1279,1460,1480 'state.kind':1260 'step':171,173,1399,1401 'step-by-step':170,1398 'strategi':1034,1154 'strict':1202 'strictfunctiontyp':1217 'strictnullcheck':1205 'string':199,201,205,207,285,344,893,898,1045,1057,1071,1119,1124,1239,1251,1286,1288 'sum':142,182,1372,1458 'switch':218,300,370,960,1259 'system':36,74,1440 'tag':276,278,333,337,619,623,634,641,711,717,734,744,987,996,1000 'team':1189 'techniqu':1394,1432 'throw':423,440,826,864,874,906,933,1073,1097 'time':95 'timestamp':917 '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' 'total':464 'touch':1176 'transact':1227 'tri':348 'true':1206,1211,1218,1268 'try/catch':553 'txnstate':1231,1257 'type':19,21,25,40,45,48,70,73,114,128,140,143,145,152,160,166,183,194,263,274,307,320,329,384,386,396,403,408,468,565,585,616,617,621,627,708,709,715,721,834,835,851,890,920,986,992,1007,1011,1016,1020,1079,1111,1116,1121,1137,1142,1230,1283,1328,1373,1375,1388,1393,1436,1459,1464,1469,1474 'type-saf':24,151,306,385,584,1473 'typeof':1297 'typescript':2,10,33,193,222,234,246,273,328,395,614,959,988,1022,1026,1041,1053,1067,1086,1115,1136,1201,1229,1282,1327,1485 'typescript-functional-pattern':1 'u':677,683,694,777,786,788,799,809,812 'undefin':522 'unhandl':829 'union':17,43,147,181,480,1174,1456 'unit':388,390,452,568,1134 'unknown':1292 'unlock':1019 'unreach':1069,1076 'unrepresent':64,109,1481 'use':11,37,71,477,478,508,537,563,611,951,981,1036,1048,1062,1109,1162 'user':288,292,294,314,494 'userid':588,1117,1120,1129,1130 'util':644,747 'valid':577,887,1082,1346 'validateconfig':355 'valu':158,265,269,280,335,394,511,531,596,625,638,643,681,698,713,729,736,784,806,998 'variant':188,491,830,966,971,974 'variant.kind':961 'void':217 'vs':571,574,589 'wallet':209,245 'without':326 'x':823,832","prices":[{"id":"3bee3098-9baf-4c65-b164-c8cf69c0be2b","listingId":"7fc45312-3907-4d45-8d77-d732275cb1eb","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.787Z"}],"sources":[{"listingId":"7fc45312-3907-4d45-8d77-d732275cb1eb","source":"github","sourceId":"martinffx/atelier/typescript-functional-patterns","sourceUrl":"https://github.com/martinffx/atelier/tree/main/skills/typescript-functional-patterns","isPrimary":false,"firstSeenAt":"2026-05-10T07:03:13.787Z","lastSeenAt":"2026-05-18T19:05:25.340Z"}],"details":{"listingId":"7fc45312-3907-4d45-8d77-d732275cb1eb","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"martinffx","slug":"typescript-functional-patterns","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":"4051473e9ee0632b7827cabbb5e53d864a33eac4","skill_md_path":"skills/typescript-functional-patterns/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/martinffx/atelier/tree/main/skills/typescript-functional-patterns"},"layout":"multi","source":"github","category":"atelier","frontmatter":{"name":"typescript-functional-patterns","description":"Functional programming patterns for reliable TypeScript. Use when modeling state machines, discriminated unions, Result/Option types, branded types, or building type-safe domain models."},"skills_sh_url":"https://skills.sh/martinffx/atelier/typescript-functional-patterns"},"updatedAt":"2026-05-18T19:05:25.340Z"}}