skills/alinaqi/claude-bootstrap/typescript

# typescript

SKILL.md

# TypeScript Skill

Load with: base.md

## Strict Mode (Non-Negotiable)

    // tsconfig.json
    {
      "compilerOptions": {
        "strict": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noImplicitReturns": true,
        "esModuleInterop": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true
      }
    }

## Project Structure

    project/
    ├── src/
    │   ├── core/               # Pure business logic
    │   │   ├── types.ts        # Domain types/interfaces
    │   │   ├── services/       # Pure functions
    │   │   └── index.ts        # Public API
    │   ├── infra/              # Side effects
    │   │   ├── api/            # HTTP handlers
    │   │   ├── db/             # Database operations
    │   │   └── external/       # Third-party integrations
    │   └── utils/              # Shared utilities
    ├── tests/
    │   ├── unit/
    │   └── integration/
    ├── package.json
    ├── tsconfig.json
    └── CLAUDE.md

## Tooling (Required)

    // package.json scripts
    {
      "scripts": {
        "lint": "eslint src/ --ext .ts,.tsx",
        "typecheck": "tsc --noEmit",
        "test": "jest",
        "test:coverage": "jest --coverage",
        "format": "prettier --write 'src/**/*.ts'"
      }
    }

    // eslint.config.js
    import eslint from '@eslint/js';
    import tseslint from 'typescript-eslint';

    export default tseslint.config(
      eslint.configs.recommended,
      ...tseslint.configs.strictTypeChecked,
      {
        rules: {
          '@typescript-eslint/no-explicit-any': 'error',
          '@typescript-eslint/explicit-function-return-type': 'error',
          'max-lines-per-function': ['error', 20],
          'max-depth': ['error', 2],
          'max-params': ['error', 3],
        }
      }
    );

## Testing with Jest

    // tests/unit/services/user.test.ts
    import { calculateTotal } from '../../../src/core/services/pricing';

    describe('calculateTotal', () => {
      it('returns sum of item prices', () => {
        // Arrange
        const items = [{ price: 10 }, { price: 20 }];

        // Act
        const result = calculateTotal(items);

        // Assert
        expect(result).toBe(30);
      });

      it('returns zero for empty array', () => {
        expect(calculateTotal([])).toBe(0);
      });

      it('throws on invalid item', () => {
        expect(() => calculateTotal([{ invalid: 'item' }])).toThrow();
      });
    });

## GitHub Actions

    name: TypeScript Quality Gate

    on: [push, pull_request]

    jobs:
      quality:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4

          - name: Setup Node
            uses: actions/setup-node@v4
            with:
              node-version: '20'

          - name: Install dependencies
            run: npm ci

          - name: Lint
            run: npm run lint

          - name: Type Check
            run: npm run typecheck

          - name: Test with Coverage
            run: npm run test:coverage

          - name: Coverage Threshold (80%)
            run: npm run test:coverage -- --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80,"statements":80}}'

## Pre-Commit Hooks

Using Husky + lint-staged:

    npm install -D husky lint-staged
    npx husky init

    // package.json
    {
      "lint-staged": {
        "*.{ts,tsx}": [
          "eslint --fix",
          "prettier --write"
        ]
      }
    }

    # .husky/pre-commit
    npx lint-staged
    npx tsc --noEmit
    npm run test -- --onlyChanged --passWithNoTests

This runs on every commit:

1. ESLint + Prettier on staged files
2. Type check entire project
3. Tests for changed files only

## Type Patterns

### Discriminated Unions for Results

    type Result<T> =
      | { ok: true; value: T }
      | { ok: false; error: string };

    function parseUser(data: unknown): Result<User> {
      // Type-safe error handling without exceptions
    }

### Branded Types for IDs

    type UserId = string & { readonly brand: unique symbol };
    type OrderId = string & { readonly brand: unique symbol };

    // Can't accidentally pass UserId where OrderId expected
    function getOrder(orderId: OrderId): Order { ... }

### Const Assertions for Literals

    const STATUSES = ['pending', 'active', 'closed'] as const;
    type Status = typeof STATUSES[number]; // 'pending' | 'active' | 'closed'

### Zod for Runtime Validation

    import { z } from 'zod';

    const UserSchema = z.object({
      email: z.string().email(),
      name: z.string().min(1).max(100),
    });

    type User = z.infer<typeof UserSchema>;

## TypeScript Anti-Patterns

- ❌ `any` type - use `unknown` and narrow
- ❌ Type assertions (`as`) - use type guards
- ❌ Non-null assertions (`!`) - handle null explicitly
- ❌ `@ts-ignore` without explanation
- ❌ Enums - use const objects or union types
- ❌ Classes for data - use interfaces/types
- ❌ Default exports - use named exports

Weekly Installs
57
Repository
alinaqi/claude-bootstrap
First Seen
Jan 19, 2026
Security Audits
Gen Agent Trust Hub Pass
Socket Pass
Snyk Pass
Installed on
claude-code 45
opencode 42
gemini-cli 37
codex 33
cursor 32
antigravity 32