Claude
Skills
Sign in
Back

typescript-pro

Included with Lifetime
$97 forever

Strict TypeScript with zero-any tolerance, no-unsafe-* lints, floating-promise prevention, and disciplined type-system usage. Use when implementing, debugging, refactoring, or reviewing TypeScript code; resolving type errors; configuring tsconfig/ESLint/Prettier; setting up React/Next/Express patterns; eliminating any/unknown drift; or evaluating advanced generics, conditional types, and inference. Applies to any TypeScript work unless a more specific role overrides.

Web Dev

What this skill does


# TypeScript Pro

Senior-level TypeScript expertise for production projects. Focuses on strict type safety, zero-any tolerance, and TypeScript's full type system capabilities.

## When Invoked

1. Review `tsconfig.json` and `eslint.config.js` for project conventions
2. For build system setup, invoke the **just-pro** skill (covers just vs make)
3. Apply type-first development and established project patterns

## Core Standards

**Required:**
- Strict mode enabled with all compiler flags
- **NO explicit or implicit `any`** - use `unknown` and narrow
- **NO type assertions to circumvent the type system** (`as any`, `as unknown as T`)
- **NO dangling promises** - await, return, or void explicitly
- All exported functions have explicit return types
- ESLint strict-type-checked passes with project configuration
- Table-driven tests for multiple cases

**Foundational Principles:**
- **Single Responsibility**: One module = one purpose, one function = one job
- **No God Objects**: Split large classes/objects; if it has 10+ methods or properties, decompose
- **Dependency Injection**: Pass dependencies via constructor/params, don't instantiate internally
- **Small Interfaces**: Prefer many small types over few large ones; compose with intersection types
- **Composition over Inheritance**: Use object composition and mixins, not deep class hierarchies

---

## Project Setup (TypeScript 5.5+)

### Version Management

Pin Node version with [mise](https://mise.jdx.dev): `mise use node@22` (creates `.mise.toml` — commit it). Team members run `mise install`. See **mise** skill for setup.

### New Project Quick Start

```bash
# Initialize
npm init -y
npm install -D typescript typescript-eslint @eslint-community/eslint-plugin-eslint-comments eslint-plugin-sonarjs prettier lint-staged vitest

# Add scripts to package.json:
npm pkg set scripts.typecheck="tsc --noEmit"
npm pkg set scripts.lint="eslint src/"
npm pkg set scripts.test="vitest run"
npm pkg set scripts.check="npm run typecheck && npm run lint && npm run test"

# Configure lint-staged (formats only staged files on commit)
npm pkg set lint-staged --json '{"*.{ts,tsx}": ["prettier --write"], "*.{json,yml,yaml}": ["prettier --write"]}'

# Create .prettierignore (prevent formatting machine-generated and non-TS files)
cat > .prettierignore << 'EOF'
# ============================================================================
# DO NOT ADD SOURCE FILES HERE TO WORK AROUND LINE LENGTH LIMITS.
#
# If prettier expansion pushes a file past max-lines (400) or
# max-lines-per-function (60), the file needs to be DECOMPOSED — extract
# functions, split into modules, rearchitect. That is the engineering fix.
#
# Adding source files here suppresses formatting without fixing the real
# problem. The line limits are design signals, not obstacles to route around.
# ============================================================================

# Machine-generated / non-source (safe to exclude)
coverage/
dist/
node_modules/
.worktrees/
.timbers/
.beads/
EOF

# Verify
npm run check
```

### Pre-commit Hook

Quality gates run via a git pre-commit hook. With beads 1.0+, hooks live in **`.beads/hooks/`** (committed to git, managed by `bd hooks install --beads`). Beads, timbers, and your quality gates all coexist in the same hook file via section markers — content outside markers is preserved across reinstalls.

**Setup:**
1. `bd init` (creates `.beads/hooks/` and sets `core.hooksPath = .beads/hooks`)
2. `timbers hooks install` (detects `core.hooksPath`, appends into existing files alongside beads)
3. Add quality gates between the BEADS and TIMBERS marker sections — they're preserved across `bd hooks install --force` and `timbers hooks install` reruns

**Pre-commit hook structure** (`.beads/hooks/pre-commit`):
```bash
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.x ---  (managed — do not edit)
# ... bd hooks run pre-commit shim ...
# --- END BEADS INTEGRATION v1.0.x ---

# Quality gates (preserved across reinstalls — outside markers)
if [ -f .git/MERGE_HEAD ]; then
  echo "Merge commit — skipping lint-staged"
else
  npx lint-staged
fi
if command -v just >/dev/null 2>&1 && [ -f justfile ]; then
  just check
else
  npm run check
fi

# --- timbers section (managed by timbers hooks install)
# ... timbers hook run pre-commit shim ...
# --- end timbers section ---
```

**Why this order:** Beads runs first (fast: bd's internal hook handles auto-export+stage of `.beads/issues.jsonl`). Quality gates run next (slowest, may fail — but `bd export` already happened, so beads state is captured even if gates fail and the commit aborts). Timbers runs last (post-gate, post-export).

**Auto-export defaults (beads 1.0+):**
- `export.auto = true` — every `bd` mutation writes `.beads/issues.jsonl` (60s throttled)
- `export.git-add = true` — auto-stages it
- The pre-commit hook forces a flush, so commits always carry current state

**Justfile recipe:**
```just
hooks:
    @bd hooks install --force --beads >/dev/null && echo "  ✅ beads hooks installed"
    @if command -v timbers >/dev/null 2>&1; then \
        timbers hooks install >/dev/null && echo "  ✅ timbers hooks installed"; \
    fi
    @current=$(git config --get core.hooksPath 2>/dev/null || true); \
    if [ "$current" != ".beads/hooks" ]; then \
        git config core.hooksPath .beads/hooks; \
        echo "  ✅ core.hooksPath fixed to .beads/hooks (was $current)"; \
    fi
```

**Note on `core.hooksPath`:** `bd hooks install --force` may set this to an absolute path. Force it relative — worktrees share repo config, and an absolute path won't resolve from a worktree's working dir.

**New dev/agent onboarding:** `git clone <repo> && just setup` (which includes `just hooks`).

If a project currently uses husky, migrate:
```bash
npm uninstall husky && rm -rf .husky && npm pkg delete scripts.prepare
bd hooks install --force --beads
git config core.hooksPath .beads/hooks
# Move any custom hook content into .beads/hooks/<hook> outside the BEADS markers
```

### Monorepo Variant

In monorepos (multiple packages, possibly mixed languages), adjust the setup:

**lint-staged: scoped to TS packages only.** Don't format Go/Rust code with Prettier — they have their own formatters (goimports, rustfmt).

```bash
# Root package.json (npm workspaces / turborepo):
npm pkg set lint-staged --json '{"packages/web/**/*.{ts,tsx}": ["prettier --write"], "*.{json,yml,yaml}": ["prettier --write"]}'

# Or independent packages (no workspaces): install lint-staged per TS package
```

**Pre-commit: lint-staged only, no `npm run check`.** Full quality gates across all packages are too slow for pre-commit. Run lint-staged in the hook, run full gates via `just check` or CI.

```bash
# .beads/hooks/pre-commit (between BEADS markers and timbers section):
npx lint-staged
```

(beads 1.0+ auto-exports + auto-stages `.beads/issues.jsonl` on every mutation; the manual export+stage lines older docs showed are no longer needed.)

For mixed-language monorepos without workspaces, detect which packages have staged files:

```bash
if git diff --cached --name-only | grep -q '^packages/web/'; then
  (cd packages/web && npx lint-staged)
fi
```

**`.prettierrc`: root-level.** Prettier walks up the directory tree, so a single root config covers all TS packages. Use per-package configs only if packages need different formatting.

**Required Config Files:** Copy `references/gitignore` → `.gitignore`, `references/prettierrc.json` → `.prettierrc`, then create `tsconfig.json` and `eslint.config.js` per the templates below.

### Developer Onboarding

```bash
git clone <repo> && cd <repo>
just setup               # Runs mise trust/install + npm ci
just check               # Verify everything works
```

Or manually:
```bash
mise trust && mise install  # Get pinned Node version
npm ci                      # Get dependencies
```

**Why strict configs?** Type errors caught at compile time are 10x cheaper than runtime bugs. Strict linting prevents `any` 

Related in Web Dev