jcnavarrete/typescript-rules icon
public
Published on 5/22/2025
Typescript Rules

Rules

Adhere to these TypeScript coding best practices and guardrails for high-quality, maintainable, scalable, and robust applications. These guidelines foster a "top senior TypeScript expert Agent" mindset, emphasizing type safety, clarity, and modern standards.

TypeScript Coding Best Practices and Guardrails

1. Embrace Strict Type Safety:

  • Eliminate any: Strictly avoid any. Figure out the right typing; if not possible, use unknown, object, or Record<string, unknown>. Cast unknown values only after runtime type checks.

  • Explicit Typing: Explicitly declare types for function parameters, return values, and complex objects to improve readability and prevent subtle errors.

  • Leverage Advanced Types: Utilize generics, conditional types, mapped types, discriminated unions, and utility types (e.g., Partial<T>, Required<T>) for robust type safety and reusability.

  • strictNullChecks: Always enable strictNullChecks in tsconfig.json. Use optional chaining (?.) and nullish coalescing (??).

2. Adhere to ESLint and Code Style:

  • Respect ESLint Configuration: Strictly follow all rules in eslint.config.ts (or .eslintrc.js), including @typescript-eslint/recommended and @typescript-eslint/recommended-requiring-type-checking.

  • Automated Formatting: Integrate a code formatter (e.g., Prettier) for consistent style.

  • Avoid Unused Code: Eliminate unused imports, variables, functions, and classes. ESLint rules should enforce this.

  • Consistent Naming: Use clear, descriptive, and consistent naming (e.g., PascalCase for types, camelCase for variables/functions).

3. Code Structure and Modularity:

  • Single Responsibility Principle (SRP): Each file, function, or class should have a single, well-defined responsibility.

  • Modular Design: Break features into smaller, independent, testable, and reusable modules.

  • Clear Exports/Imports: Be explicit about exports and imports. Prefer named exports for clarity.

  • Interface vs. Type Aliases: Prefer interface for object shapes; type for unions, intersections, and primitive aliases.

4. Immutability and Side Effects:

  • Prefer const and readonly: Use const for unchangeable references; readonly for properties not reassigned after initialization.

  • Minimize Side Effects: Design pure functions where possible. Make necessary side effects explicit and contained.

5. Error Handling and Robustness:

  • Graceful Error Handling: Implement robust try...catch blocks for async operations and potential runtime errors.

  • Custom Error Types: Define custom error classes for specific application errors, providing context for targeted handling.

  • Type Guards: Use type guards (instanceof, typeof, user-defined) to narrow types in conditional blocks, ensuring safety with polymorphic data.

6. Asynchronous Programming:

  • Proper Promise Typing: Always type Promises (e.g., Promise<MyDataType>) for correct resolved value typing.

  • Async/Await: Prefer async/await for cleaner, more readable asynchronous code.

7. Documentation and Readability:

  • JSDoc for Public APIs: Document public functions, classes, and interfaces with JSDoc for clear descriptions and IDE support.

  • Meaningful Comments: Use comments sparingly, focusing on why something is done.

  • Code Simplicity: Strive for the simplest possible solution; avoid over-engineering.

8. Performance Considerations (Type System):

  • Avoid Excessive Type Complexity: Overly complex or deeply nested types can impact TypeScript compilation. Prioritize clarity and maintainability.

  • Type Inference Optimization: Be aware that large union types or deeply nested conditional types can slow type inference.

9. Testing:

  • Unit and Integration Tests: Write comprehensive tests. Compile-time type safety doesn't replace runtime validation.

  • Test-Driven Development (TDD): Consider TDD to drive design and ensure testability.

10. Dependency Management and External Libraries:

  • Type Definitions: Ensure all third-party libraries have proper type definitions (@types/library-name). Create local .d.ts files if needed.

  • Version Control: Pin dependencies to specific versions to avoid unexpected breaking changes.

Consistently applying these principles leads to robust, type-safe TypeScript code and cultivates a disciplined, senior-level software architect approach.