Install
$ agentstack add skill-everyone-needs-a-copilot-claude-copilot-javascript-patterns ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
JavaScript Patterns
Modern JavaScript/TypeScript patterns, anti-patterns, and quality rules.
Core Principles
| Principle | Description | |-----------|-------------| | Immutability | Prefer const, avoid mutation | | Pure Functions | Same input = same output, no side effects | | Async/Await | Over raw promises and callbacks | | Type Safety | Use TypeScript for non-trivial projects |
Patterns vs Anti-Patterns
Variable Declaration
// GOOD: const by default
const config = { timeout: 5000 };
const items = ['a', 'b', 'c'];
// OK: let when reassignment needed
let count = 0;
for (const item of items) {
count++;
}
// BAD: var (hoisting issues)
var data = fetchData(); // Never use var
Async/Await
// GOOD: async/await
async function fetchUsers(): Promise {
try {
const response = await fetch('/api/users');
return await response.json();
} catch (error) {
throw new ApiError('Failed to fetch users', { cause: error });
}
}
// GOOD: Parallel with Promise.all
const [users, posts] = await Promise.all([
fetchUsers(),
fetchPosts()
]);
// BAD: Sequential when parallel possible
const users = await fetchUsers();
const posts = await fetchPosts(); // Waits unnecessarily
Error Handling
// GOOD: Custom error classes
class ValidationError extends Error {
constructor(
message: string,
public field: string,
public code: string
) {
super(message);
this.name = 'ValidationError';
}
}
// GOOD: Error boundary with type narrowing
function isApiError(error: unknown): error is ApiError {
return error instanceof ApiError;
}
try {
await riskyOperation();
} catch (error) {
if (isApiError(error)) {
handleApiError(error);
} else {
throw error; // Re-throw unknown errors
}
}
// BAD: Catch and ignore
try {
await riskyOperation();
} catch (e) {
// Silent failure - never do this
}
Nullish Handling
// GOOD: Nullish coalescing
const value = input ?? defaultValue; // Only null/undefined
// GOOD: Optional chaining
const name = user?.profile?.name;
const result = callback?.();
// BAD: OR for defaults (falsy issues)
const value = input || defaultValue; // 0, '', false become default!
// BAD: Manual null checks
const name = user && user.profile && user.profile.name;
Array Methods
// GOOD: Functional array methods
const activeUsers = users
.filter(u => u.active)
.map(u => ({ id: u.id, name: u.name }));
// GOOD: find/findIndex for single items
const admin = users.find(u => u.role === 'admin');
const index = users.findIndex(u => u.id === targetId);
// GOOD: reduce for accumulation
const byId = users.reduce((acc, user) => {
acc[user.id] = user;
return acc;
}, {} as Record);
// BAD: forEach with mutation
const results = [];
users.forEach(u => {
if (u.active) results.push(u.name);
});
Object Operations
// GOOD: Spread for immutable updates
const updated = { ...user, name: 'New Name' };
const merged = { ...defaults, ...options };
// GOOD: Destructuring
const { id, name, email } = user;
const { data, error } = await fetchUser(id);
// GOOD: Computed property names
const key = 'dynamicKey';
const obj = { [key]: value };
// BAD: Object.assign mutation
Object.assign(user, { name: 'New Name' }); // Mutates!
String Operations
// GOOD: Template literals
const message = `Hello, ${name}! You have ${count} items.`;
const multiline = `
First line
Second line
`;
// GOOD: Tagged templates for escaping
const query = sql`SELECT * FROM users WHERE id = ${userId}`;
// BAD: Concatenation
const message = 'Hello, ' + name + '!';
Anti-Patterns to Avoid
Type Coercion Issues
// BAD: Implicit coercion
if (value == null) { } // Catches both null and undefined
const str = '' + num; // Use String(num)
// GOOD: Explicit comparison
if (value === null || value === undefined) { }
if (value == null) { } // OK - intentional loose equality for null/undefined
// GOOD: Explicit conversion
const str = String(num);
const num = Number(str);
const bool = Boolean(value);
Callback Hell
// BAD: Nested callbacks
getData((data) => {
process(data, (result) => {
save(result, (saved) => {
notify(saved, () => {
console.log('Done');
});
});
});
});
// GOOD: async/await
const data = await getData();
const result = await process(data);
const saved = await save(result);
await notify(saved);
Floating Promises
// BAD: Unhandled promise
fetchData(); // Fire and forget - errors lost!
// GOOD: Handle or await
await fetchData();
// OR
fetchData().catch(handleError);
// OR
void fetchData(); // Explicit discard (use sparingly)
this Binding Issues
// BAD: Lost context
class Handler {
name = 'Handler';
handleClick() {
console.log(this.name); // undefined when used as callback!
}
}
// GOOD: Arrow function or bind
class Handler {
name = 'Handler';
handleClick = () => {
console.log(this.name); // Works!
};
}
TypeScript Best Practices
Type Definitions
// GOOD: Interface for objects
interface User {
id: string;
name: string;
email: string;
}
// GOOD: Type alias for unions/primitives
type Status = 'pending' | 'active' | 'archived';
type ID = string | number;
// GOOD: Generics for reusable types
type Result =
| { success: true; data: T }
| { success: false; error: E };
Type Guards
// Discriminated unions
interface SuccessResponse { status: 'success'; data: unknown }
interface ErrorResponse { status: 'error'; message: string }
type Response = SuccessResponse | ErrorResponse;
function handleResponse(res: Response) {
if (res.status === 'success') {
// TypeScript knows res.data exists
return res.data;
} else {
// TypeScript knows res.message exists
throw new Error(res.message);
}
}
Avoid any
// BAD: any disables type checking
function process(data: any): any { }
// GOOD: unknown + type guard
function process(data: unknown): Result {
if (isValidData(data)) {
return transform(data);
}
throw new ValidationError('Invalid data');
}
Quality Checklist
| Check | Rule | |-------|------| | No var | Use const by default, let when needed | | No any | Use unknown with type guards | | Async/await | Over raw promises | | Nullish ops | ?? and ?. over || and && | | Immutable | Spread over mutation | | Type safety | All exports typed | | Error handling | No silent catches | | No floating promises | Always handle or await |
Invocation — JavaScript Anti-Pattern Checker (L3 Script)
Run the checker on any JavaScript or TypeScript source file. Consume its output only — the script source never enters context.
Scope note: This is a regex-based lint-lite tool, not a full AST parser. It reliably catches the named closed-set patterns (var declarations, loose equality, leftover debug calls, callback nesting depth). It does NOT replace ESLint — use ESLint for comprehensive coverage. Treat its findings as confirmed anti-patterns; treat its silence as "none of these specific patterns found."
Run via Bash (file argument):
python .claude/skills/code/javascript-patterns/scripts/js_patterns.py path/to/file.js
Run via Bash (stdin — paste code or pipe):
cat path/to/file.ts | python .claude/skills/code/javascript-patterns/scripts/js_patterns.py -
The script outputs:
- A JSON object with
findings(list of anti-patterns with rule, severity, line, message) and asummaryof counts by severity. - A human-readable markdown table sorted by severity descending.
Detected rules:
VAR_DECLMEDIUM —vardeclaration (use const/let)LOOSE_EQUALITYMEDIUM —==or!=operator (use===/!==)CONSOLE_LOGLOW — leftoverconsole.log(callCALLBACK_NESTINGMEDIUM — callback nesting depth >= 3 (likely callback hell)
Error handling: Script exits 1 on unreadable file. Exits 0 even if findings are present.
What the agent does with the output:
- Raise MEDIUM findings in code review comments.
- LOW findings (console.log) flag debug code left in — ask author to remove before merge.
- CALLBACK_NESTING findings suggest refactoring to async/await.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: Everyone-Needs-A-Copilot
- Source: Everyone-Needs-A-Copilot/claude-copilot
- License: MIT
- Homepage: https://ineedacopilot.com
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.