effect-patterns-value-handling
Improve code reliability by using Option types for explicit value handling in Effect-TS.
Install
mkdir -p .claude/skills/effect-patterns-value-handling && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/6078" && unzip -o skill.zip -d .claude/skills/effect-patterns-value-handling && rm skill.zipInstalls to .claude/skills/effect-patterns-value-handling
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Effect-TS patterns for Value Handling. Use when working with value handling in Effect-TS applications.Key capabilities
- →Replace null or undefined with Option types
- →Perform pattern matching on optional data
- →Transform optional values using map and flatMap
- →Provide fallback values with getOrElse
- →Filter optional values using predicates
- →Combine multiple Options into a single result
How it works
The skill uses the Effect-TS Option module to wrap values in Some or None containers. It forces explicit handling of missing data through pattern matching and functional combinators.
Inputs & outputs
When to use effect-patterns-value-handling
- →Refactoring code to replace null with Option
- →Implementing pattern matching for optional data
- →Building type-safe value workflows
About this skill
Effect-TS Patterns: Value Handling
This skill provides 2 curated Effect-TS patterns for value handling. Use this skill when working on tasks related to:
- value handling
- Best practices in Effect-TS applications
- Real-world patterns and solutions
🟡 Intermediate Patterns
Optional Pattern 1: Handling None and Some Values
Rule: Use Option to represent values that may not exist, replacing null/undefined with type-safe Option that forces explicit handling.
Good Example:
This example demonstrates Option handling patterns.
import { Effect, Option } from "effect";
interface User {
id: string;
name: string;
email: string;
}
interface Profile {
bio: string;
website?: string;
location?: string;
}
const program = Effect.gen(function* () {
console.log(
`\n[OPTION HANDLING] None/Some values and pattern matching\n`
);
// Example 1: Creating Options
console.log(`[1] Creating Option values:\n`);
const someValue: Option.Option<string> = Option.some("data");
const noneValue: Option.Option<string> = Option.none();
const displayOption = <T,>(opt: Option.Option<T>, label: string) =>
Effect.gen(function* () {
if (Option.isSome(opt)) {
yield* Effect.log(`${label}: Some(${opt.value})`);
} else {
yield* Effect.log(`${label}: None`);
}
});
yield* displayOption(someValue, "someValue");
yield* displayOption(noneValue, "noneValue");
// Example 2: Creating from nullable values
console.log(`\n[2] Converting nullable to Option:\n`);
const possiblyNull = (shouldExist: boolean): string | null =>
shouldExist ? "found" : null;
const toOption = (value: string | null | undefined): Option.Option<string> =>
value ? Option.some(value) : Option.none();
const opt1 = toOption(possiblyNull(true));
const opt2 = toOption(possiblyNull(false));
yield* displayOption(opt1, "toOption(found)");
yield* displayOption(opt2, "toOption(null)");
// Example 3: Pattern matching on Option
console.log(`\n[3] Pattern matching with match():\n`);
const userId: Option.Option<string> = Option.some("user-123");
const message = Option.match(userId, {
onSome: (id) => `User ID: ${id}`,
onNone: () => "No user found",
});
yield* Effect.log(`[MATCH] ${message}`);
const emptyUserId: Option.Option<string> = Option.none();
const emptyMessage = Option.match(emptyUserId, {
onSome: (id) => `User ID: ${id}`,
onNone: () => "No user found",
});
yield* Effect.log(`[MATCH] ${emptyMessage}\n`);
// Example 4: Transforming with map
console.log(`[4] Transforming values with map():\n`);
const userCount: Option.Option<number> = Option.some(42);
const doubled = Option.map(userCount, (count) => count * 2);
yield* displayOption(doubled, "doubled");
// Chaining maps
const email: Option.Option<string> = Option.some("[email protected]");
const domain = Option.map(email, (e) =>
e.split("@")[1] ?? "unknown"
);
yield* displayOption(domain, "email domain");
// Example 5: Chaining with flatMap
console.log(`\n[5] Chaining operations with flatMap():\n`);
const findUser = (id: string): Option.Option<User> =>
id === "user-1"
? Option.some({ id, name: "Alice", email: "[email protected]" })
: Option.none();
const getProfile = (userId: string): Option.Option<Profile> =>
userId === "user-1"
? Option.some({ bio: "Developer", website: "alice.dev" })
: Option.none();
const userId2 = Option.some("user-1");
// Chained operations: userId -> user -> profile
const profileChain = Option.flatMap(userId2, (id) =>
Option.flatMap(findUser(id), (user) =>
getProfile(user.id)
)
);
const profileResult = Option.match(profileChain, {
onSome: (profile) => `Bio: ${profile.bio}, Website: ${profile.website}`,
onNone: () => "No profile found",
});
yield* Effect.log(`[CHAIN] ${profileResult}\n`);
// Example 6: Fallback values with getOrElse
console.log(`[6] Default values with getOrElse():\n`);
const optionalStatus: Option.Option<string> = Option.none();
const status = Option.getOrElse(optionalStatus, () => "unknown");
yield* Effect.log(`[DEFAULT] Status: ${status}`);
// Real value
const knownStatus: Option.Option<string> = Option.some("active");
const realStatus = Option.getOrElse(knownStatus, () => "unknown");
yield* Effect.log(`[VALUE] Status: ${realStatus}\n`);
// Example 7: Filter with predicate
console.log(`[7] Filtering with conditions:\n`);
const ageOption: Option.Option<number> = Option.some(25);
const isAdult = Option.filter(ageOption, (age) => age >= 18);
yield* displayOption(isAdult, "Adult check (25)");
const ageOption2: Option.Option<number> = Option.some(15);
const isAdult2 = Option.filter(ageOption2, (age) => age >= 18);
yield* displayOption(isAdult2, "Adult check (15)");
// Example 8: Multiple Options (all present?)
console.log(`\n[8] Combining multiple Options:\n`);
const firstName: Option.Option<string> = Option.some("John");
const lastName: Option.Option<string> = Option.some("Doe");
const middleName: Option.Option<string> = Option.none();
// All three present?
const allPresent = Option.all([firstName, lastName, middleName]);
yield* displayOption(allPresent, "All present");
// Just two
const twoPresent = Option.all([firstName, lastName]);
yield* displayOption(twoPresent, "Two present");
// Example 9: Converting Option to Error
console.log(`\n[9] Converting Option to Result/Error:\n`);
const optionalConfig: Option.Option<{ apiKey: string }> = Option.none();
const configOrError = Option.match(optionalConfig, {
onSome: (config) => config,
onNone: () => {
throw new Error("Configuration not found");
},
});
// In real code, would catch error
const result = Option.match(optionalConfig, {
onSome: (config) => ({ success: true, value: config }),
onNone: () => ({ success: false, error: "config-not-found" }),
});
yield* Effect.log(`[CONVERT] ${JSON.stringify(result)}\n`);
// Example 10: Option in business logic
console.log(`[10] Practical: Optional user settings:\n`);
const userSettings: Option.Option<{
theme: string;
notifications: boolean;
}> = Option.some({
theme: "dark",
notifications: true,
});
const getTheme = Option.map(userSettings, (s) => s.theme);
const theme = Option.getOrElse(getTheme, () => "light"); // Default
yield* Effect.log(`[SETTING] Theme: ${theme}`);
// No settings
const noSettings: Option.Option<{ theme: string; notifications: boolean }> =
Option.none();
const noTheme = Option.map(noSettings, (s) => s.theme);
const defaultTheme = Option.getOrElse(noTheme, () => "light");
yield* Effect.log(`[DEFAULT] Theme: ${defaultTheme}`);
});
Effect.runPromise(program);
Rationale:
Option enables null-safe programming:
- Some(value): Value exists
- None: Value doesn't exist
- Pattern matching: Handle both cases
- Chaining: Compose operations safely
- Fallbacks: Default values
- Conversions: Option ↔ Error
Pattern: Use Option.isSome(), Option.isNone(), match(), map(), flatMap()
Null/undefined causes widespread bugs:
Problem 1: Billion-dollar mistake
- Tony Hoare invented null in ALGOL in 1965
- Created "billion-dollar mistake"
- 90% of security vulnerabilities involve null handling
Problem 2: Undefined behavior
user.profile.name- any property could be null- Runtime error: "Cannot read property 'name' of undefined"
- No compile-time warning
- Production crash
Problem 3: Silent failures
- Function returns null on failure
- Caller doesn't check
- Uses null as if it's a value
- Corrupts state downstream
Problem 4: Conditional hell
if (user !== null && user.profile !== null && user.profile.name !== null) {
// Do thing
}
Solutions:
Option type:
Some(value)= value existsNone= value doesn't exist- Type system forces checking
- No silent null checks possible
Pattern matching:
Option.match()- Handle both cases explicitly
- Compiler warns if you miss one
Chaining:
option.map().flatMap().match()- Pipeline of operations
- Null-safe by design
🟠 Advanced Patterns
Optional Pattern 2: Optional Chaining and Composition
Rule: Use Option combinators (map, flatMap, ap) to compose operations that may fail, creating readable and maintainable pipelines.
Good Example:
This example demonstrates optional chaining patterns.
import { Effect, Option, pipe } from "effect";
interface User {
id: string;
name: string;
email: string;
}
interface Profile {
bio: string;
website?: string;
avatar?: string;
}
interface Settings {
theme: "light" | "dark";
notifications: boolean;
language: string;
}
const program = Effect.gen(function* () {
console.log(`\n[OPTIONAL CHAINING] Composing Option operations\n`);
// Example 1: Simple chain with map
console.log(`[1] Chaining transformations with map():\n`);
const userId: Option.Option<string> = Option.some("user-42");
const userDisplayId = Option.map(userId, (id) => `User#${id}`);
const idMessage = Option.match(userDisplayId, {
onSome: (display) => display,
onNone: () => "No user ID",
});
yield* Effect.log(`[CHAIN 1] ${idMessage}`);
// Chained maps
const email: Option.Option<string> = Option.some("[email protected]");
const emailParts = pipe(
email,
Option.map((e) => e.toLowerCase()),
Option.map((e) => e.split("@")),
Option.map((parts) => parts[0]) // username
);
const username = Option.getOrElse(emailParts, () => "unknown");
yield* Effect.log(`[USERNAME] ${username}\n`);
// Example 2: FlatMap for chaining operations that return Option
console.log(`[2] Chaining operations with flatMap():\n`);
const findUser = (id: string): Option.Option<User> =>
id === "user-42"
---
*Content truncated.*
When not to use it
- →When working with non-Effect-TS applications
- →When simple null checks are sufficient for the codebase
Prerequisites
Limitations
- →Requires adoption of Effect-TS patterns
- →Adds complexity to simple data structures
How it compares
Unlike standard null checks which are prone to runtime errors, this approach uses a type system to ensure all cases are handled at compile time.
Compared to similar skills
effect-patterns-value-handling side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| effect-patterns-value-handling (this skill) | 1 | 7mo | No flags | Intermediate |
| typescript-review | 39 | 2mo | No flags | Intermediate |
| typescript | 28 | 2mo | No flags | Beginner |
| react-patterns | 9 | 6mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
More by PaulJPhilp
View all by PaulJPhilp →You might also like
typescript-review
metabase
Review TypeScript and JavaScript code changes for compliance with Metabase coding standards, style violations, and code quality issues. Use when reviewing pull requests or diffs containing TypeScript/JavaScript code.
typescript
lobehub
TypeScript code style and optimization guidelines. Use when writing TypeScript code (.ts, .tsx, .mts files), reviewing code quality, or implementing type-safe patterns. Triggers on TypeScript development, type safety questions, or code style discussions.
react-patterns
davila7
Modern React patterns and principles. Hooks, composition, performance, TypeScript best practices.
antfu
antfu
Anthony Fu's opinionated tooling and conventions for JavaScript/TypeScript projects. Use when setting up new projects, configuring ESLint/Prettier alternatives, monorepos, library publishing, or when the user mentions Anthony Fu's preferences.
obsidian-sdk-patterns
jeremylongshore
Apply production-ready Obsidian plugin patterns for TypeScript. Use when implementing complex features, refactoring plugins, or establishing coding standards for Obsidian development. Trigger with phrases like "obsidian patterns", "obsidian best practices", "obsidian code patterns", "idiomatic obsidian plugin".
ast-grep-find
parcadei
AST-based code search and refactoring via ast-grep MCP