Skip to content
Back to skills

Typescript Rules

ASecurity

TypeScript/JavaScript coding rules: style, patterns, security, testing. Triggers: .ts, .tsx, .js, .jsx, package.json, tsconfig.json, React, Next.js, Vue, Vite, Vitest, Jest, ESLint.

  • 177 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
developmentjavascripttypescriptrustjavasqlreactvueangularnextjsnode

Works with

  • cli
  • api

Security analysis

A100/100

Scanned May 27, 2026

npx -y skills add softspark/ai-toolkit --skill typescript-rules --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Typescript Rules?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Typescript Rules
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/softspark-typescript-rules/badge)](https://www.skillsdirectory.com/skills/softspark-typescript-rules)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: typescript-rules
description: "TypeScript/JavaScript coding rules: style, patterns, security, testing. Triggers: .ts, .tsx, .js, .jsx, package.json, tsconfig.json, React, Next.js, Vue, Vite, Vitest, Jest, ESLint."
effort: medium
user-invocable: false
allowed-tools: Read
---

# TypeScript/JavaScript Rules

These rules come from `app/rules/typescript/` in ai-toolkit. They cover
the project's standards for coding style, frameworks, patterns,
security, and testing in TypeScript/JavaScript. Apply them when writing or
reviewing TypeScript/JavaScript code.

# TypeScript Coding Style

## Strict Mode
- Always use `strict: true` in tsconfig.json.
- Never use `any` -- use `unknown` + type guards instead.
- Prefer `interface` over `type` for object shapes (extendable).
- Use `as const` for literal types and readonly tuples.

## Naming
- PascalCase: types, interfaces, enums, classes, components.
- camelCase: variables, functions, methods, properties.
- UPPER_SNAKE: constants, env vars.
- Prefix interfaces with `I` only if project convention requires it.

## Functions
- Prefer arrow functions for callbacks and inline.
- Use `function` declarations for hoisted, named functions.
- Max 3 parameters -- use options object beyond that.
- Always type return values for public/exported functions.

## Imports
- Group: node builtins, external, internal, relative.
- Use `type` imports: `import type { Foo } from './foo'`.
- No barrel exports unless at package boundary.
- Prefer named exports over default exports.

## Types
- Use discriminated unions over class hierarchies for state.
- Use `readonly` for arrays and objects that should not be mutated.
- Use `satisfies` operator to validate types without widening.
- Prefer `unknown` over `any` at API boundaries.
- Use template literal types for string patterns.

## Avoid
- `enum` -- use `as const` objects or union types.
- `namespace` -- use ES modules.
- `private` keyword -- use `#` private fields.
- Non-null assertion `!` -- use proper type narrowing.
- `as` type casting -- use type guards and narrowing.

## Configuration
- Enable `noUncheckedIndexedAccess` for safer array/object access.
- Enable `exactOptionalPropertyTypes` to distinguish `undefined` from missing.
- Use `moduleResolution: "bundler"` for modern projects.
- Set `isolatedModules: true` for bundler compatibility.

# TypeScript Frameworks

## React
- Use function components exclusively. No class components.
- Colocate state with the component that owns it. Lift only when needed.
- Use `useCallback` and `useMemo` only when profiling shows a need.
- Use `React.lazy()` + Suspense for code-splitting routes.
- Avoid prop drilling past 2 levels -- use Context or state management.

## Next.js (App Router)
- Default to Server Components. Add `"use client"` only when needed.
- Use Server Actions for mutations. Never expose internal APIs to client.
- Use `loading.tsx` and `error.tsx` for streaming and error boundaries.
- Fetch data in Server Components, not in useEffect on client.
- Use `revalidatePath` / `revalidateTag` for cache invalidation.

## Express / Fastify / Hono
- Use layered architecture: route -> controller -> service -> repository.
- Validate request body/params/query with Zod middleware.
- Centralize error handling in a single error middleware.
- Use async route handlers with proper error forwarding.
- Return consistent response shapes: `{ data }` or `{ error }`.

## State Management
- Use Zustand or Jotai for client state. Redux only for complex existing apps.
- Use TanStack Query (React Query) for server state.
- Separate server state (fetched data) from client state (UI state).
- Never duplicate server data in client state stores.

## ORM / Database
- Use Drizzle for new projects (SQL-like, type-safe, lightweight).
- Use Prisma for rapid prototyping (schema-first, great DX).
- Always use migrations. Never modify schema manually in production.
- Use transactions for multi-table operations.

## Node.js Runtime
- Use `node:` prefix for built-in modules: `import { readFile } from 'node:fs/promises'`.
- Prefer `fetch` (built-in since Node 18) over axios/node-fetch.
- Use `structuredClone()` for deep cloning.
- Set `"type": "module"` in package.json for ESM.

## Monorepo
- Use Turborepo or Nx for monorepo orchestration.
- Share types via internal packages, not copy-paste.
- Use workspace protocols: `"@org/shared": "workspace:*"`.

# TypeScript Patterns

## Error Handling
- Use Result type pattern: `{ success: true; data: T } | { success: false; error: E }`.
- Use Zod `.safeParse()` for validation -- returns typed result, never throws.
- Create domain-specific error classes extending `Error` with error codes.
- Centralize error handling in middleware, not in each handler.
- Never catch errors silently. Log or rethrow with context.

## Discriminated Unions
- Use discriminated unions for state machines and polymorphic data.
- Always include a `type` or `kind` literal field as discriminant.
- Use `switch` with exhaustive checking (`never` in default) on unions.
- Prefer unions over optional fields for mutually exclusive states.

## Async Patterns
- Use `async/await` everywhere. Never use raw `.then()` chains.
- Use `Promise.all()` for independent concurrent operations.
- Use `Promise.allSettled()` when some failures are acceptable.
- Implement cancellation with `AbortController` for long operations.
- Wrap callbacks in Promises at the boundary, then use async/await.

## Validation
- Validate at API boundaries with Zod, Valibot, or ArkType.
- Derive TypeScript types from schemas: `z.infer<typeof Schema>`.
- Never trust runtime data to match TypeScript types without validation.
- Use branded types for domain primitives: `UserId`, `Email`, `Slug`.

## Dependency Injection
- Use constructor injection for services and repositories.
- Accept interfaces, not concrete classes, in constructors.
- Use factory functions for creating configured instances.
- Avoid service locator pattern and global singletons.

## Immutability
- Use `readonly` on interface properties by default.
- Use `Readonly<T>`, `ReadonlyArray<T>` for function parameters.
- Use `Object.freeze()` only for runtime safety in config objects.
- Prefer spread/map/filter over mutating methods (push, splice).

## Type Guards
- Use `is` return type for custom type guards: `(x: unknown): x is User`.
- Use `in` operator for discriminating object shapes.
- Prefer `satisfies` over `as` for type validation without casting.
- Use assertion functions (`asserts x is T`) for preconditions.

# TypeScript Security

## Input Validation
- Validate ALL request data with Zod/Valibot at API boundary.
- Use `.strip()` / `.strict()` to reject unexpected fields.
- Validate URL params and query strings, not just request bodies.
- Never pass raw user input to `eval()`, `Function()`, or template literals in SQL.

## XSS Prevention
- Use framework auto-escaping (React JSX, Angular templates).
- Never use `dangerouslySetInnerHTML` without DOMPurify sanitization.
- Sanitize user content before storing, not just before rendering.
- Set CSP headers: `default-src 'self'; script-src 'self'`.

## Authentication
- Use `httpOnly`, `secure`, `sameSite: 'strict'` for auth cookies.
- Implement CSRF protection for cookie-based auth (double-submit or token).
- Use short-lived JWTs (15min) with refresh token rotation.
- Validate JWT signature, expiration, issuer, and audience on every request.

## SQL / NoSQL Injection
- Use parameterized queries with Drizzle/Prisma/TypeORM. Never concatenate.
- For raw queries, use tagged template literals: `sql\`SELECT * FROM users WHERE id = ${id}\``.
- Validate and cast IDs to expected types (UUID, integer) before queries.
- Use ORM query builders for dynamic filters.

## Dependency Security
- Run `npm audit` in CI. Fail on high/critical vulnerabilities.
- Use `npm audit signatures` to verify package provenance.
- Pin exact versions with lockfile. Review lockfile changes in PRs.
- Avoid packages with postinstall scripts unless trusted.

## Secrets
- Use `process.env` with Zod validation for env vars.
- Never import `.env` files in production -- use platform env injection.
- Never log `req.headers.authorization` or session tokens.
- Use `crypto.timingSafeEqual()` for comparing secrets.

## Server Hardening
- Set security headers: HSTS, X-Content-Type-Options, X-Frame-Options.
- Use `helmet` middleware in Express, built-in security in Fastify.
- Implement rate limiting on all endpoints (`express-rate-limit`, `@fastify/rate-limit`).
- Disable `X-Powered-By` header. Do not expose server technology.

## File Uploads
- Validate file type by magic bytes, not just extension or MIME type.
- Set maximum file size limits on the server.
- Store uploads outside the web root. Serve through a proxy with CDN.
- Generate random filenames. Never use user-provided filenames for storage.

# TypeScript Testing

## Framework
- Use Vitest for new projects (faster, native ESM, TypeScript-first).
- Use Jest only for existing projects already using it.
- Use Playwright for E2E browser testing.
- Use Supertest or built-in fetch for API integration tests.

## File Naming
- Test files: `*.test.ts` or `*.spec.ts` colocated with source.
- Test utilities: `tests/helpers/` or `tests/utils/`.
- Fixtures: `tests/fixtures/` with typed factory functions.

## Structure
- Use `describe` for grouping by function/class/feature.
- Use `it` with behavior descriptions: `it('returns 404 when user not found')`.
- Avoid deeply nested `describe` blocks (max 2 levels).
- Use `beforeEach` for setup, avoid `beforeAll` for mutable state.

## Type-Safe Mocking
- Use `vi.fn()` with type parameters: `vi.fn<[string], Promise<User>>()`.
- Use `vi.mock()` for module-level mocking.
- Prefer dependency injection over module mocking for testability.
- Use `vi.spyOn()` for partial mocks on existing objects.

## React/Component Testing
- Use React Testing Library. Query by role, label, text -- not test IDs.
- Use `userEvent` over `fireEvent` for realistic user interactions.
- Test behavior and rendered output, not component internals.
- Use `renderHook` for testing custom hooks in isolation.

## Assertions
- Use `expect().toBe()` for primitives, `expect().toEqual()` for objects.
- Use `expect().toMatchInlineSnapshot()` for complex output verification.
- Avoid `toBeTruthy/toBeFalsy` -- use specific matchers.
- Use `expect().rejects.toThrow()` for async error testing.

## Async Testing
- Always `await` async operations. Never use `done` callback.
- Use `vi.useFakeTimers()` for timer-dependent code.
- Use `waitFor` from Testing Library for async DOM updates.

## Performance
- Run tests in parallel (Vitest default). Isolate state to enable this.
- Use `vi.mock()` for heavy dependencies (DB, network) in unit tests.
- Keep unit test suite under 30 seconds.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.