Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    citypaul

    typescript-strict

    citypaul/typescript-strict
    Coding
    510
    2 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    TypeScript strict mode patterns. Use when writing any TypeScript code.

    SKILL.md

    TypeScript Strict Mode

    Core Rules

    1. No any - ever. Use unknown if type is truly unknown
    2. No type assertions (as Type) without justification
    3. Prefer type over interface for data structures
    4. Reserve interface for behavior contracts only

    Type vs Interface

    type — for data structures

    export type User = {
      readonly id: string;
      readonly email: string;
      readonly name: string;
      readonly roles: ReadonlyArray<string>;
    };
    

    Why type? Better for unions, intersections, mapped types. readonly signals immutability. More flexible composition with utility types.

    interface — for behavior contracts

    export interface UserRepository {
      findById(id: string): Promise<User | undefined>;
      save(user: User): Promise<void>;
    }
    

    Why interface? Signals "this must be implemented." Works with implements keyword. Conventional for dependency injection.

    Schema Duplication

    Define schemas once, import everywhere. Never duplicate the same validation logic across multiple files.

    // ✅ Define once
    export const CreateUserRequestSchema = z.object({
      email: z.string().email(),
      name: z.string().min(1),
    });
    export type CreateUserRequest = z.infer<typeof CreateUserRequestSchema>;
    
    // Import and use wherever needed
    

    Strict Mode Configuration

    tsconfig.json Settings

    {
      "compilerOptions": {
        "strict": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "noUnusedLocals": true,
        "noUnusedParameters": true,
        "noImplicitReturns": true,
        "noFallthroughCasesInSwitch": true,
        "noUncheckedIndexedAccess": true,
        "exactOptionalPropertyTypes": true,
        "noPropertyAccessFromIndexSignature": true,
        "forceConsistentCasingInFileNames": true,
        "allowUnusedLabels": false
      }
    }
    

    What Each Setting Does

    Core strict flags:

    • strict: true - Enables all strict type checking options
    • noImplicitAny - Error on expressions/declarations with implied any type
    • strictNullChecks - null and undefined have their own types (not assignable to everything)
    • noUnusedLocals - Error on unused local variables
    • noUnusedParameters - Error on unused function parameters
    • noImplicitReturns - Error when not all code paths return a value
    • noFallthroughCasesInSwitch - Error on fallthrough cases in switch statements

    Additional safety flags (CRITICAL):

    • noUncheckedIndexedAccess - Array/object access returns T | undefined (prevents runtime errors from assuming elements exist)
    • exactOptionalPropertyTypes - Distinguishes property?: T from property: T | undefined (more precise types)
    • noPropertyAccessFromIndexSignature - Requires bracket notation for index signature properties (forces awareness of dynamic access)
    • forceConsistentCasingInFileNames - Prevents case sensitivity issues across operating systems
    • allowUnusedLabels - Error on unused labels (catches accidental labels that do nothing)

    Additional Rules

    • No @ts-ignore without explicit comments explaining why
    • These rules apply to test code as well as production code

    Architectural Insight: noUnusedParameters Catches Design Issues

    The noUnusedParameters rule can reveal architectural problems:

    Example: A function with an unused parameter often indicates the parameter belongs in a different layer. Strict mode catches these design issues early.


    Immutability, Pure Functions, and Composition

    For detailed patterns on immutability (readonly, ReadonlyArray), pure functions, composition, Result types, array methods, and factory functions, see the functional skill. These are the canonical patterns used across the codebase.

    Key TypeScript-specific notes:

    • Use readonly on all type properties and ReadonlyArray<T> for arrays
    • The compiler enforces immutability when readonly is used — leverage this
    • Factory functions (not classes) for object creation, supporting dependency injection

    Schema-First at Trust Boundaries

    When Schemas ARE Required

    • Data crosses trust boundary (external → internal)
    • Type has validation rules (format, constraints)
    • Shared data contract between systems
    • Used in test factories (validate test data completeness)
    // API responses, user input, external data
    const UserSchema = z.object({
      id: z.string().uuid(),
      email: z.string().email(),
    });
    type User = z.infer<typeof UserSchema>;
    
    // Validate at boundary
    const user = UserSchema.parse(apiResponse);
    

    When Schemas AREN'T Required

    • Pure internal types (utilities, state)
    • Result/Option types (no validation needed)
    • TypeScript utility types (Partial<T>, Pick<T>, etc.)
    • Behavior contracts (interfaces - structural, not validated)
    • Component props (unless from URL/API)
    // ✅ CORRECT - No schema needed
    type Result<T, E> =
      | { success: true; data: T }
      | { success: false; error: E };
    
    // ✅ CORRECT - Interface, no validation
    interface UserService {
      createUser(user: User): void;
    }
    

    Branded Types

    For type-safe primitives:

    type UserId = string & { readonly brand: unique symbol };
    type PaymentAmount = number & { readonly brand: unique symbol };
    
    // Type-safe at compile time
    const processPayment = (userId: UserId, amount: PaymentAmount) => {
      // Implementation
    };
    
    // ❌ Can't pass raw string/number
    processPayment('user-123', 100); // Error
    
    // ✅ Must use branded type
    const userId = 'user-123' as UserId;
    const amount = 100 as PaymentAmount;
    processPayment(userId, amount); // OK
    

    Summary Checklist

    When writing TypeScript code, verify:

    • No any types - using unknown where type is truly unknown
    • No type assertions without justification
    • Using type for data structures with readonly
    • Using interface for behavior contracts
    • Schemas defined once, not duplicated
    • Strict mode enabled with all checks passing
    • For immutability, pure functions, composition: see functional skill
    Recommended Servers
    Vercel Grep
    Vercel Grep
    Prisma
    Prisma
    Cloudflare
    Cloudflare
    Repository
    citypaul/.dotfiles
    Files