home / skills / adibfirman / dotfiles / js-ts-fp

This skill helps you apply functional programming principles to TypeScript and JavaScript by analyzing codebases first and guiding pure, immutable design.

npx playbooks add skill adibfirman/dotfiles --skill js-ts-fp

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
5.2 KB
---
name: js-ts-fp
description: Write TypeScript and JavaScript code like a top engineer using functional programming principles. Use when writing new code, reviewing existing code, or refactoring TS/JS projects. Applies pure functions, immutability, function composition, higher-order functions, declarative style, and avoiding shared state using native patterns only (no external libraries). Always analyzes the existing codebase first to understand patterns and conventions before making changes or suggestions.
---

# Functional Programming Engineering Skill

Write and review code using functional programming principles like a top engineer.

## Workflow

### Step 1: Analyze the Codebase

Before writing or reviewing any code, examine the repository:

1. **Confirm TypeScript or JavaScript**: Check for `.ts`, `.tsx`, `.js`, `.jsx` files and `package.json`
2. **Find existing patterns**: Look at 2-3 representative files to understand current conventions
3. **Check tsconfig.json**: Note strict mode, module system, and target ES version

### Step 2: Apply FP Principles

Apply these principles (all natively supported in TS/JS):

| Principle              | Description                               |
| ---------------------- | ----------------------------------------- |
| Pure functions         | No side effects, same input → same output |
| Immutability           | Never mutate, always return new values    |
| Declarative style      | Describe what, not how                    |
| Function composition   | Build complex from simple functions       |
| Higher-order functions | Functions that take/return functions      |
| Avoid shared state     | No globals, no mutation of external state |
| Discriminated unions   | TypeScript pattern matching alternative   |

### Step 3: Execute Task

**Writing new code:**

- Follow existing repo conventions for file structure and naming
- Use FP patterns consistent with what's already in the codebase
- If no FP patterns exist, introduce them gradually and idiomatically

**Reviewing code:**

- Identify imperative patterns that could be functional
- Flag mutation, side effects, shared state
- Suggest specific refactors with before/after examples

**Refactoring:**

- Preserve behavior while improving structure
- Transform loops → map/filter/reduce
- Extract pure functions from impure ones
- Isolate side effects to boundaries

## Core FP Transformations

### Imperative → Declarative

```
// Before: imperative loop
let results = [];
for (let i = 0; i < items.length; i++) {
  if (items[i].active) {
    results.push(transform(items[i]));
  }
}

// After: declarative
const results = items
  .filter(item => item.active)
  .map(transform);
```

### Mutation → Immutability

```
// Before: mutation
function addItem(cart, item) {
  cart.items.push(item);
  cart.total += item.price;
  return cart;
}

// After: immutable
function addItem(cart, item) {
  return {
    ...cart,
    items: [...cart.items, item],
    total: cart.total + item.price
  };
}
```

### Shared State → Pure Functions

```
// Before: shared state
let counter = 0;
function increment() {
  counter++;
  return counter;
}

// After: pure
function increment(counter) {
  return counter + 1;
}
```

### Nested Logic → Composition

```
// Before: nested
function process(data) {
  const validated = validate(data);
  if (validated) {
    const transformed = transform(validated);
    return format(transformed);
  }
  return null;
}

// After: composed (with pipe/flow)
const process = pipe(
  validate,
  transform,
  format
);
```

## Detailed Patterns Reference

For comprehensive patterns including Option/Result types, composition helpers, currying, and TypeScript-specific techniques, see [references/patterns.md](references/patterns.md).

## Code Review Checklist

When reviewing, check for:

- [ ] Functions return values (not void/undefined for logic)
- [ ] No mutation of input parameters
- [ ] Side effects isolated and clearly marked
- [ ] Loops replaced with map/filter/reduce where clearer
- [ ] Conditionals use early returns or ternaries for simple cases
- [ ] Complex conditionals extracted to named predicates
- [ ] State transformations are pure
- [ ] Error handling uses Result/Either/Option patterns when available

## Anti-Patterns to Flag

| Anti-Pattern             | Refactor To                       |
| ------------------------ | --------------------------------- |
| `for` loop with push     | `map`/`filter`/`reduce`           |
| `let` with reassignment  | `const` with transformation       |
| Nested callbacks         | Composition or async/await        |
| `null` checks everywhere | Option/Maybe type                 |
| `try/catch` everywhere   | Result/Either type                |
| Class with mutable state | Pure functions + data             |
| Global variables         | Dependency injection              |
| `if/else` chains         | Pattern matching or lookup tables |

## When NOT to Apply FP

- Performance-critical hot paths where mutation is measurably faster
- When the team/codebase has no FP experience (introduce gradually)
- Simple scripts where FP adds complexity without benefit
- When existing patterns in the repo are intentionally imperative
- Legacy codebases where consistency matters more than FP purity

Overview

This skill helps you write, review, and refactor TypeScript and JavaScript using practical functional programming patterns. It focuses on pure functions, immutability, composition, and avoiding shared state while respecting existing repository conventions. Use it to incrementally introduce FP patterns without adding external libraries.

How this skill works

First, I analyze the codebase: detect TS/JS files, inspect representative files, and check tsconfig settings to understand conventions and strictness. Then I apply native FP techniques—pure functions, immutability, higher-order functions, and composition—while preserving behavior. For reviews I flag imperative patterns and provide concrete before/after refactors. For refactors I isolate side effects to boundaries and convert loops and mutations into declarative pipelines.

When to use it

  • Writing new TypeScript/JavaScript modules where long-term maintainability matters
  • Reviewing PRs to remove hidden mutations, side effects, or shared state
  • Refactoring legacy code to improve predictability and testability
  • Introducing FP patterns gradually in teams with limited FP experience
  • Converting loops and stateful logic into map/filter/reduce and composed functions

Best practices

  • Always inspect 2–3 representative files and tsconfig.json before changing style
  • Prefer pure functions and return new values instead of mutating inputs
  • Use map/filter/reduce and small composable helpers instead of loops
  • Keep side effects at the edges (I/O, logging) and mark them clearly
  • Use discriminated unions and Result/Option patterns in TypeScript for safer branches

Example use cases

  • Refactor a shopping cart implementation to remove in-place mutations and return new cart objects
  • Review a PR that uses global counters and suggest pure alternatives with explicit state passing
  • Rewrite a nested transformation pipeline into composable functions using pipe/flow
  • Convert complex conditional chains into named predicates and discriminated unions
  • Introduce an Option/Result pattern to replace scattered null checks and try/catch usage

FAQ

Will this introduce third-party FP libraries?

No. All recommendations use native JavaScript/TypeScript features; libraries are only suggested if the team explicitly requests them.

How do you handle performance-sensitive code?

I avoid blind FP application on hot paths. If mutation measurably improves performance, I document trade-offs and keep intent explicit.

What if the codebase is intentionally imperative?

I follow existing conventions. When FP is introduced, I recommend gradual, low-risk changes and provide before/after examples for review.