| name | typescript-javascript |
| description | TypeScript and JavaScript development standards for modern web and Node.js development. Covers strict TypeScript configuration, type safety patterns, ESM modules, async/await, testing with Jest/Vitest, and security best practices. Use when working with .ts, .tsx, .js, .mjs files, package.json, tsconfig.json, or when asking about TypeScript/JavaScript best practices. |
TypeScript & JavaScript Development
Guiding Principles
- Type Safety: Leverage strict mode, avoid
any, use discriminated unions
- Explicit Over Implicit: Prefer explicit types for clarity and maintainability
- Modern Defaults: ESM, const/let, async/await, optional chaining
- Security First: Never use
eval, sanitize HTML, validate inputs
Quick Reference
| Aspect | TypeScript | JavaScript |
|---|
| Package Manager | pnpm preferred | pnpm preferred |
| Module System | ES Modules | ES Modules + // @ts-check |
| Linting | eslint --max-warnings=0 | eslint --max-warnings=0 |
| Formatting | Prettier | Prettier |
| Types | Strict mode | JSDoc types |
Non-Negotiables
NN-1: Validate at trust boundaries
Static types do not validate runtime data. Treat HTTP bodies, responses, webhooks, queue messages, localStorage/sessionStorage, environment variables, CLI args, and JSON files as unknown until validated with Zod or an explicit type guard.
Reject:
JSON.parse(raw) as T
await response.json() as T
- unchecked property access on remote JSON
as any or double assertions (value as unknown as T)
NN-2: Fetch/API calls require timeout + status check + schema validation
Every service/network fetch must include a timeout, check response.ok, and validate the JSON shape before returning typed data.
import { z } from 'zod';
async function fetchJson<T>(
url: string,
schema: z.ZodType<T>,
options: RequestInit = {},
): Promise<T> {
const response = await fetch(url, {
...options,
signal: options.signal ?? AbortSignal.timeout(5_000),
headers: { Accept: 'application/json', ...options.headers },
});
if (!response.ok) {
throw new Error(`request failed: HTTP ${response.status}`);
}
return schema.parse(await response.json());
}
NN-3: Production servers are hardened by default
Node/Express/Fastify services need bounded request bodies, explicit CORS, security headers, request IDs, structured logging, generic 5xx responses, and graceful shutdown. Do not use direct app.listen(...) examples for production services without retaining and closing the server.
NN-4: Package manager and runtime are pinned
Declare packageManager and Node engine policy in package.json; CI uses Corepack and the declared package manager.
{
"packageManager": "pnpm@10.0.0",
"engines": { "node": ">=22" }
}
Critical Patterns
if (value === 0) { }
if (value == 0) { }
fetchData().catch(err => console.error(err));
const name = user?.profile?.name ?? 'Guest';
const seen = new Set();
const seen = [];
eval(userInput);
element.textContent = userInput;
element.innerHTML = userInput;
TypeScript Configuration
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noUncheckedIndexedAccess": true,
"esModuleInterop": true,
"skipLibCheck": false
}
}
Avoid any - Use Proper Types
function processData(data: any): any {
return data.value;
}
function processData<T>(data: T): T {
return data;
}
function processData(data: unknown): string {
if (typeof data === 'object' && data !== null && 'value' in data) {
return String((data as { value: unknown }).value);
}
throw new Error('Invalid data');
}
Discriminated Unions
type SuccessResponse = {
status: 'success';
data: { id: string; name: string };
};
type ErrorResponse = {
status: 'error';
error: { code: number; message: string };
};
type ApiResponse = SuccessResponse | ErrorResponse;
function handleResponse(response: ApiResponse): void {
if (response.status === 'success') {
console.log(response.data.id);
} else {
console.log(response.error.message);
}
}
Utility Types
interface User {
id: string;
name: string;
email: string;
password: string;
}
type UserUpdate = Partial<User>;
type UserCredentials = Pick<User, 'email' | 'password'>;
type UserPublic = Omit<User, 'password'>;
type RequiredUser = Required<User>;
type ReadonlyUser = Readonly<User>;
Async Patterns
const [users, products] = await Promise.all([
fetchUsers(),
fetchProducts()
]);
const results = await Promise.allSettled([
fetchUsers(),
fetchProducts()
]);
results.forEach(result => {
if (result.status === 'fulfilled') {
console.log(result.value);
} else {
console.error(result.reason);
}
});
Security Rules (Mandatory)
- Never use
eval, new Function, or unsanitized innerHTML
- Use
textContent for DOM insertion
- Validate and sanitize all external inputs
- Do not log secrets/tokens/PII
- Use parameterized queries; no string-built queries
- Enforce HTTPS; secure cookies (HttpOnly, SameSite)
Naming Conventions
| Type | Convention | Example |
|---|
| Functions/Variables | camelCase | fetchUserData |
| Classes/Interfaces | PascalCase | UserService |
| Constants | UPPER_SNAKE_CASE | MAX_RETRIES |
| Types | PascalCase | ApiResponse |
Detailed References