home / skills / phrazzld / claude-config / typescript-excellence

typescript-excellence skill

/skills/typescript-excellence

This skill helps you enforce TypeScript excellence by applying strict typings, safe patterns, and modern toolchain guidelines across projects.

npx playbooks add skill phrazzld/claude-config --skill typescript-excellence

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

Files (5)
SKILL.md
3.4 KB
---
name: typescript-excellence
description: |
  TypeScript best practices, type safety, and toolchain standards. Use when:
  - Writing or reviewing TypeScript code
  - Setting up TypeScript projects or tsconfig
  - Choosing state management patterns
  - Configuring build tools (tsup, Vitest)
  - Avoiding type gymnastics or any-abuse
  Keywords: TypeScript, tsconfig, strict mode, no-any, pnpm, Vitest, tsup,
  TanStack Query, discriminated unions, type safety, ESLint
effort: high
---

# TypeScript Excellence

Type-safe, maintainable TypeScript with modern toolchain.

## Type Safety

**Never use `any`**. Alternatives:
```typescript
// Unknown for external data
function parse(input: unknown): User { ... }

// Union for specific options
type Status = 'loading' | 'success' | 'error';

// Generics for reusable code
function first<T>(arr: T[]): T | undefined { ... }

// Type assertion as last resort (with runtime check)
if (isUser(data)) { return data as User; }
```

**tsconfig.json essentials:**
```json
{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true
  }
}
```

## Module Organization

Feature-based, not layer-based:
```
src/
  features/
    auth/
      components/
      hooks/
      api.ts
      types.ts
      index.ts  # Barrel: controlled exports
    orders/
      ...
  shared/
    ui/
    utils/
```

Use path aliases: `@features/*`, `@shared/*` (avoid `../../../../`).

## State Management

**Server state**: TanStack Query exclusively.
```typescript
const { data, isLoading, error } = useQuery({
  queryKey: ['user', userId],
  queryFn: () => fetchUser(userId),
});
```

**Client state**: Discriminated unions.
```typescript
type AuthState =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'authenticated'; user: User }
  | { status: 'error'; error: AppError };
```

## Async Patterns

Always wrap, always context:
```typescript
async function fetchUser(id: string): Promise<User> {
  try {
    const res = await fetch(`/api/users/${id}`);
    if (!res.ok) throw new ApiError(res.status, await res.text());
    return res.json();
  } catch (error) {
    throw new AppError('Failed to fetch user', { cause: error });
  }
}
```

Cancellation: Use `AbortController` for long operations. Always clean up timeouts in `finally` blocks.

## Toolchain

| Tool | Purpose |
|------|---------|
| **pnpm** | Package manager (declare in `packageManager` field) |
| **Vitest** | Testing (unit/integration/e2e) |
| **tsup** | Builds (ESM + CJS + .d.ts) |
| **ESLint** | Linting (`@typescript-eslint/no-explicit-any: error`) |
| **Prettier** | Formatting |

## Anti-Patterns

- Type gymnastics (conditional types, template literals without justification)
- `useState` + `useEffect` for server data (use TanStack Query)
- Technical-layer folders (`/controllers`, `/services`, `/models`)
- `eslint-disable` without documented justification
- Mocking internal components (mock boundaries only)
- Mixed package managers or test frameworks

## References

- [toolchain-setup.md](references/toolchain-setup.md) - pnpm, Vitest, tsup, ESLint config
- [type-patterns.md](references/type-patterns.md) - Utility types, generics, guards
- [testing-strategy.md](references/testing-strategy.md) - Test pyramid, behavior focus
- [async-patterns.md](references/async-patterns.md) - Timeout cleanup, error-specific catch, cancellation

Overview

This skill codifies TypeScript best practices to deliver type-safe, maintainable code and a modern toolchain. It focuses on strict compiler settings, feature-based module organization, clear state patterns, and pragmatic async handling. Use it to standardize project setup, reviews, and implementation patterns for robust TypeScript projects.

How this skill works

It enforces strict type-safety practices (avoid any, prefer unknown, unions, generics, and runtime guards) and recommends a tsconfig that enables strict flags and safer indexed access. It prescribes feature-based folders, path aliases, and explicit export barrels. The skill also prescribes state handling (TanStack Query for server state, discriminated unions for client state), async error/cancellation patterns, and a cohesive toolchain (pnpm, tsup, Vitest, ESLint, Prettier).

When to use it

  • Starting a new TypeScript project and defining tsconfig and tooling
  • Reviewing or refactoring TypeScript code to remove unsafe types and anti-patterns
  • Designing module layout and path alias strategies for large apps
  • Choosing state management patterns for server vs client state
  • Configuring build and test tooling (tsup, Vitest, pnpm, ESLint)

Best practices

  • Enable strict compiler flags (strict, noImplicitAny, noUncheckedIndexedAccess, exactOptionalPropertyTypes)
  • Avoid any; use unknown, unions, generics, or runtime type guards instead
  • Organize code by feature with controlled barrel exports and path aliases to reduce relative paths
  • Use TanStack Query for all server state and discriminated unions for client UI state
  • Wrap async calls with try/catch, throw rich errors, and support cancellation with AbortController
  • Enforce lint rules that ban implicit any and document any eslint-disable usages

Example use cases

  • Set up tsconfig and project scaffolding for a new repo with pnpm, tsup, Vitest, ESLint, and Prettier
  • Review a pull request that introduces transient any types and replace with safe alternatives and guards
  • Refactor a monolithic src layout into feature-based folders and add @features/* path aliases
  • Implement data fetching with TanStack Query and predictable error handling and cancellation
  • Define client auth state as a discriminated union to simplify UI handling of loading/error/authenticated states

FAQ

What should I do when external data shapes are unknown?

Accept raw inputs as unknown, validate with runtime type guards, then narrow to typed domain objects. Avoid asserting to a type without a check.

When is a type assertion acceptable?

Only as a last resort after performing a runtime check. Prefer guards, generics, or unions first; assertions without checks are a source of bugs.