7.6 KiB
7.6 KiB
TypeScript Code Guidelines
- Write clear, readable, maintainable code first.
- Prefer modern TypeScript and current ECMAScript syntax.
- Keep code simple and explicit.
- Avoid clever or overly abstract solutions.
- Follow the existing project style and architecture.
- Enable strict TypeScript settings where possible.
- Prefer
strict: true. - Avoid
any. - Prefer precise types over broad types.
- Prefer
unknownoveranyfor untrusted or unknown values. - Narrow types explicitly before use.
- Prefer strongly typed models over loose objects.
- Avoid excessive type assertions.
- Avoid non-null assertions unless safety is guaranteed.
- Prefer discriminated unions for state and variant modeling.
- Prefer literal unions over arbitrary strings.
- Prefer enums only when they provide a clear benefit.
- Prefer
readonlywhere mutation is not required. - Prefer immutable data where practical.
- Avoid unnecessary mutation.
- Avoid global mutable state.
- Keep functions small and focused.
- Keep modules focused on a clear responsibility.
- Prefer composition over inheritance.
- Avoid unnecessary classes when functions and plain objects are sufficient.
- Avoid unnecessary interfaces and abstractions.
- Prefer concrete object types when no abstraction is required.
- Use interfaces primarily when extension, implementation contracts, or declaration merging is actually useful.
- Prefer
typealiases for unions, compositions, mapped types, and local data models. - Avoid unnecessary wrapper types.
- Avoid excessive generic complexity.
- Keep generic constraints explicit and meaningful.
- Avoid deeply nested conditional or mapped types unless they provide clear value.
- Prefer readable types over type-system cleverness.
- Avoid duplicated type definitions.
- Derive types from existing sources when practical.
- Do not duplicate backend contracts manually when generated or shared types are available.
- Prefer explicit return types for public APIs and non-trivial functions.
- Prefer inference for simple local variables.
- Avoid unnecessary annotations when inference is obvious.
- Prefer
constby default. - Use
letonly when reassignment is required. - Avoid
var. - Prefer optional chaining and nullish coalescing where appropriate.
- Do not use
||when0,false, or empty strings are valid values. - Handle
nullandundefinedintentionally. - Avoid mixing
nullandundefinedwithout a clear convention. - Prefer early returns over excessive nesting.
- Avoid deeply nested control flow.
- Avoid overly large functions.
- Avoid hidden side effects.
- Keep pure logic pure where practical.
- Prefer deterministic behavior.
- Avoid implicit runtime coercion.
- Use strict equality.
- Avoid unnecessary object spreading in hot paths.
- Avoid unnecessary array copies.
- Avoid unnecessary intermediate arrays.
- Avoid repeated
.map(),.filter(), or.reduce()chains in performance-critical paths. - Use array methods when they improve readability and performance is not critical.
- Prefer appropriate data structures such as
MapandSetwhen lookup behavior requires them. - Avoid O(n²) patterns when a better structure is practical.
- Measure performance before micro-optimizing.
- Avoid unnecessary JSON serialization and parsing.
- Avoid unnecessary deep cloning.
- Avoid
JSON.parse(JSON.stringify(...))as a cloning strategy. - Prefer platform APIs such as
structuredClonewhen deep cloning is actually required. - Avoid synchronous blocking operations in server-side hot paths.
- Prefer asynchronous APIs for I/O.
- Always handle rejected promises.
- Avoid floating promises.
- Use
awaitwhen sequencing is required. - Use
Promise.allfor independent operations when safe. - Limit concurrency for large workloads.
- Avoid uncontrolled
Promise.allover very large collections. - Support cancellation with
AbortSignalwhere applicable. - Clean up event listeners, timers, subscriptions, streams, and resources.
- Avoid memory leaks caused by retained closures or listeners.
- Do not swallow exceptions.
- Preserve useful error context.
- Use typed domain errors where they improve handling.
- Avoid exceptions for normal control flow.
- Validate external input at system boundaries.
- Never trust API payloads, query parameters, environment variables, storage data, or user input based solely on TypeScript types.
- Use runtime validation for untrusted external data.
- Do not assume compile-time types provide runtime safety.
- Prefer schema validation when external contracts are complex.
- Never expose secrets, tokens, credentials, or sensitive data.
- Do not log sensitive information.
- Avoid unsafe dynamic property access.
- Avoid
eval,Function, and dynamic code execution. - Avoid unsafe HTML injection.
- Sanitize untrusted HTML when rendering HTML is required.
- Avoid weakening TLS, authentication, authorization, CSP, validation, or other security controls.
- Treat all external data as untrusted.
- Prefer secure defaults.
- Do not disable security checks for convenience.
- Avoid prototype pollution risks when merging untrusted objects.
- Prefer allowlists over denylists for security-sensitive validation.
- Keep dependencies minimal.
- Prefer built-in platform APIs over adding small utility dependencies.
- Do not add dependencies without a clear reason.
- Avoid abandoned or unnecessary packages.
- Keep dependencies current and supported.
- Respect lockfiles.
- Do not manually modify generated lockfile content.
- Avoid broad dependency upgrades unrelated to the task.
- Use ESM or the project's existing module system consistently.
- Avoid mixing module systems without necessity.
- Prefer named exports unless the project convention favors default exports.
- Keep import paths consistent.
- Remove unused imports and exports.
- Avoid circular dependencies.
- Keep frontend, domain, application, and infrastructure concerns separated where applicable.
- Keep API and transport models separate from domain logic when necessary.
- Do not leak implementation details through public APIs.
- Avoid unnecessary public exports.
- Keep module boundaries intentional.
- Write code that is easy to test.
- Prefer behavior-based tests.
- Avoid tests coupled to implementation details.
- Mock only external boundaries or expensive dependencies where practical.
- Keep tests deterministic.
- Avoid time-, network-, and environment-dependent tests without proper isolation.
- Remove dead code instead of keeping it as commented-out code.
- Do not add comments unless the user explicitly requests them.
- Use descriptive naming.
- Avoid vague names such as
data,obj,item,temp, orvaluewhen more precise names are available. - Avoid abbreviations unless they are established domain terminology.
- Keep naming conventions consistent.
- Treat lint and TypeScript errors seriously.
- Do not suppress ESLint or TypeScript diagnostics without a concrete reason.
- Avoid
@ts-ignore. - Prefer
@ts-expect-erroronly when the error is intentional and documented. - Do not disable lint rules globally for local problems.
- Prefer correctness, security, maintainability, and predictable behavior over micro-optimizations.
Performance Priority
Prioritize performance work in this order:
- Architecture
- Network and API usage
- Database access
- Algorithms and data structures
- Rendering and state updates
- Concurrency
- Memory allocations
- CPU micro-optimizations
Never optimize based purely on assumptions.
Safety Rule
- Compile-time type safety is not runtime validation.
- Validate all external and untrusted data.
- Do not use
any, type assertions, non-null assertions, or disabled compiler checks to bypass real type problems. - Prefer making invalid states unrepresentable where practical.