AgentStack
SKILL verified MIT Self-run

Javascript Patterns

skill-everyone-needs-a-copilot-claude-copilot-javascript-patterns · by Everyone-Needs-A-Copilot

>-

No reviews yet
0 installs
15 views
0.0% view→install

Install

$ agentstack add skill-everyone-needs-a-copilot-claude-copilot-javascript-patterns

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No 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.

Are you the author of Javascript Patterns? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

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:

  1. A JSON object with findings (list of anti-patterns with rule, severity, line, message) and a summary of counts by severity.
  2. A human-readable markdown table sorted by severity descending.

Detected rules:

  • VAR_DECL MEDIUM — var declaration (use const/let)
  • LOOSE_EQUALITY MEDIUM — == or != operator (use ===/!==)
  • CONSOLE_LOG LOW — leftover console.log( call
  • CALLBACK_NESTING MEDIUM — 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:

  1. Raise MEDIUM findings in code review comments.
  2. LOW findings (console.log) flag debug code left in — ask author to remove before merge.
  3. 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.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.