javascript-development
Develop, refactor, and review modern JavaScript (ES2022+) applications using standard ECMAScript features. Use when writing ES modules, managing async flows with Promises and async/await, understandin
SKILL.mdskills/javascript-development/SKILL.md
GitHubModern JavaScript Development
Write clean, robust, and performant JavaScript using modern ECMAScript standards (ES2022+). Favor ES Modules (ESM), strict mode, asynchronous coordination with async/await, immutable state operations, and proper error boundaries.
Core Rules
- Use ES Modules (ESM) by default: Always use standard
importandexportrather than CommonJSrequire(). - Always declare variables with
constorlet: Never usevar. Preferconstfor immutability; useletonly for reassigned loop or accumulator variables. - Always use strict equality (
===and!==): Never use loose equality (==or!=) to avoid unexpected type coercion. - Never ignore unhandled Promise rejections: Always handle Promise errors with
.catch()ortry/catcharoundawait. - Protect against
nullandundefinedsafely: Use optional chaining (?.) and nullish coalescing (??) instead of truthiness hacks (||). - Understand the Event Loop: Keep microtasks short; never perform synchronous CPU loops that block the main thread.
1. Modern Syntax and Data Operations
Optional Chaining and Nullish Coalescing
// Safely access deeply nested properties
const port = config?.server?.port ?? 8080;
// Calling optional methods
user.logger?.info?.("Request received");
Structured Clone & Immutability
Do not mutate function arguments or shared objects. Use structuredClone() for deep copies:
// Deep clone nested objects and arrays
const deepCopy = structuredClone(originalState);
// Shallow updates with object spread
const updatedUser = {
...currentUser,
lastLogin: new Date().toISOString(),
};
2. Asynchronous Programming and Promises
Promise.allSettled vs Promise.all
- Use
Promise.allwhen all operations must succeed (fails fast on first error). - Use
Promise.allSettledwhen operations are independent and you need to inspect all results:
export async function syncServices(services) {
const results = await Promise.allSettled(
services.map(s => s.ping())
);
const healthy = results.filter(r => r.status === "fulfilled").map(r => r.value);
const failures = results.filter(r => r.status === "rejected").map(r => r.reason);
return { healthy, failures };
}
Avoiding Async in Array Methods
Methods like Array.prototype.forEach do NOT await promises!
// BUG: forEach does not await; requests run uncoordinated in background
// users.forEach(async user => await sendEmail(user));
// CORRECT: Use for...of loop for sequential execution
for (const user of users) {
await sendEmail(user);
}
// OR Promise.all for concurrent execution
await Promise.all(users.map(user => sendEmail(user)));
3. Error Handling and Custom Errors
Extend Error and pass the underlying cause using the standard cause option (ES2022):
export class ApiError extends Error {
constructor(message, statusCode, options = {}) {
super(message, options);
this.name = "ApiError";
this.statusCode = statusCode;
}
}
export async function fetchUserData(userId) {
try {
const res = await fetch(`/api/users/${userId}`);
if (!res.ok) {
throw new ApiError(`HTTP request failed with status ${res.status}`, res.status);
}
return await res.json();
} catch (err) {
if (err instanceof ApiError) {
throw err;
}
// Wrap low-level network failure with cause
throw new ApiError(`Network failed while fetching user ${userId}`, 500, { cause: err });
}
}
4. Common Mistakes and Anti-Patterns
| Anti-Pattern | Bug / Risk | Correct Practice |
|---|---|---|
| `val | defaultValue` | |
Floating promises without await or .catch() |
Unhandled rejection crashes Node process | Always await or return promise chain |
== null checks |
Inadvertently coerces unrelated types | Use strict check: val === null || val === undefined |
Modifying Array.prototype |
Global prototype pollution breaking third-party libraries | Never pollute built-in prototypes |
| Synchronous file I/O in web handlers | Freezes server event loop for all concurrent users | Use fs.promises / asynchronous methods |
5. Verification Checklist
- Zero usage of
varin entire codebase. - All Promise calls are handled with
awaitor.catch(). - No loose equality (
==/!=). - Error objects include meaningful error messages and original
cause. - Code passes ESLint/Prettier verification cleanly.