js-ts-performance-readability

Write JavaScript/TypeScript that is performant, readable, and easy to trust with minimal inspection. Use when generating or reviewing JS/TS code—scripts, utilities, data processing, or performance-sensitive logic—so outputs are correct, fast, and maintainable without heavy manual review.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "js-ts-performance-readability" with this command: npx skills add icyjoseph/agent-skills/icyjoseph-agent-skills-js-ts-performance-readability

JavaScript/TypeScript performance, readability, and trust

Apply these practices so generated code is correct, performant, and readable with minimal inspection.

Goals

  • Correctness: Explicit types, no silent coercion, clear edge handling.
  • Performance: Prefer Map/Set for lookups, avoid redundant work, use appropriate data structures and async patterns.
  • Readability: Small named helpers, modular structure, predictable control flow.
  • Trust: Code that type-checks and follows consistent patterns is easier to verify at a glance.

1. Model the problem first

Before writing code, understand what you're solving and what can go wrong.

Clarify inputs and outputs

  • What is the input? File, API response, user input, command-line args? What's the shape and size?
  • What is the expected output? A transformed value, a file, a side effect? What's the contract?
  • What are the constraints? Size limits, time constraints, memory concerns?

Identify edge cases upfront

Think through edge cases before implementation—they're easier to handle when designed for, not patched in:

  • Empty input: Empty string, empty array, null/undefined. What should happen?
  • Single element: Does your logic assume at least 2 items? Handle the 1-item case.
  • Boundary values: Zero, negative numbers, MAX_SAFE_INTEGER, very long strings.
  • Invalid/malformed input: Missing fields, wrong types, unexpected formats.
  • Duplicates: Does your logic handle or assume uniqueness?
  • Order sensitivity: Does input order matter? What about sorted vs unsorted?

Design tests before or alongside code

  • Write test cases (or at least list them) for: happy path, edge cases, error cases.
  • For scripts, define example inputs and expected outputs before implementing.
  • If the problem is complex, start with the simplest case that exercises the core logic, get that working, then expand.

When in doubt, ask

If requirements are ambiguous, clarify before implementing. Questions like:

  • "What should happen if X is empty?"
  • "Should this throw or return a default for invalid input?"
  • "Is this expected to handle very large inputs?"

Getting alignment on edge cases early prevents rework.


2. Correctness and trust

Use explicit types

  • Prefer TypeScript with explicit types for inputs and outputs (e.g. type Config = { name: string; timeout: number; retries: number }).
  • Use as const for tuple return types so callers get narrow types: return [success, count] as const.
  • Define return types explicitly for public functions; let inference handle internal helpers.

Avoid silent coercion

  • Parse numbers explicitly: line.split(" ").map(Number) or Number(ch); validate with !isNaN(n) or Number.isNaN(n) when needed.
  • Use nullish coalescing for "default if missing": map.get(key) ?? 0, arr.find(predicate) ?? fallback.

Handle edges explicitly

  • Check "no value" with next == null (or next === undefined) and return early.
  • When mutating shared state for a branch (e.g. backtracking search), restore state after the branch so other branches see clean state.

3. Performance

Prefer Map and Set for lookups and deduplication

  • Use Set for "visited" and "already seen" (e.g. BFS/DFS, loop detection).
  • Use Map for frequency counts or keyed caches: freq.set(key, (freq.get(key) ?? 0) + 1).

Avoid redundant work

  • Parse input once; reuse the parsed structure across multiple operations or stages.
  • For recursive or repeated computations, memoize with a deterministic cache key (e.g. serialize relevant state to a string). Prefer a single cache Map keyed by string.

Use appropriate algorithms and data structures

  • BFS/DFS: explicit queue/stack and Set for visited.
  • When the problem involves distances or costs, consider precomputing distances (e.g. BFS from key nodes) then doing targeted search instead of raw recursion over the full graph.
  • Prefer .reduce((acc, n) => acc + n, 0) or a small sum(arr) helper for numeric aggregation instead of manual loops when it stays readable.

Prefer single-pass or linear structures where possible

  • Use .find(predicate) and .findLast(predicate) instead of scanning manually twice.
  • Use toSorted((a, b) => a - b) (or .slice().sort(...)) when you need a sorted copy without mutating the original.
  • Avoid chaining methods that each iterate the full array when a single pass would suffice (e.g. .filter().map() when .flatMap() or .reduce() could do both).

Async patterns: parallelize independent work

  • Promise.all: Use when you have independent async operations that can run concurrently. Fails fast—if any promise rejects, the whole result rejects.
    const [users, posts] = await Promise.all([fetchUsers(), fetchPosts()]);
    
  • Promise.allSettled: Use when you need results from all promises regardless of individual failures. Returns { status: 'fulfilled', value } or { status: 'rejected', reason } for each.
    const results = await Promise.allSettled(urls.map(fetch));
    const succeeded = results.filter(r => r.status === 'fulfilled').map(r => r.value);
    
  • Promise.race: Use when you want the first promise to settle (e.g. timeout patterns).
  • Avoid sequential awaits for independent work:
    // Bad: sequential, takes sum of times
    const a = await fetchA();
    const b = await fetchB();
    
    // Good: parallel, takes max of times
    const [a, b] = await Promise.all([fetchA(), fetchB()]);
    

Avoid common performance traps

  • Regex in loops: Compile regex once outside the loop (const re = /pattern/g), not inside.
  • Repeated JSON.parse/stringify: Parse once, pass the object; don't serialize/deserialize repeatedly.
  • Unnecessary cloning: Use spread or structuredClone only when mutation is a real concern; don't defensively clone everything.
  • String concatenation in tight loops: Use array + .join() or template literals for building large strings.

4. Readability and structure

Small named helpers

  • Extract pure helpers: sum, groupBy, keyBy, chunk, unique, clamp, debounce.
  • Keep helpers focused; name them by intent (e.g. parseConfig, validateInput, formatOutput) so the main flow reads like prose.

Modular structure

  • Separate concerns: parsing/input, processing/logic, output/formatting.
  • Use a clear entry point (e.g. main(), run(), process()) that orchestrates the stages; keep it thin so the flow is obvious.
  • Group related logic into functions or modules; avoid monolithic functions that do parsing, processing, and output all in one.

Prefer const and explicit control flow

  • Use const by default; use let only when reassignment is needed.
  • Use early returns for edge cases; avoid deep nesting.
  • In loops, use continue for "skip this item" and keep the main path obvious.

Parsing and input handling

  • Prefer declarative parsing: input.split("\n").map(line => line.split(" ").map(Number)) when the format is simple.
  • For complex parsing, use a small function or iterator so the main logic stays clear.
  • Validate input shape early; fail fast with clear error messages rather than silently producing garbage.

5. Consistency and style

  • One way to do things: Use either .reduce() or a for loop for summing, but don't mix styles randomly in the same file.
  • Naming: Use consistent names across the codebase (e.g. always visited or always seen, not both). Name variables by what they represent, not by type (users not userArray).
  • Error handling: Be consistent—either throw exceptions, return Result types, or return null/undefined, but pick one pattern per codebase or module.

6. Checklist before suggesting code

  • Problem modeled: Inputs, outputs, and edge cases identified before implementation.
  • Edge cases covered: Empty, single-element, boundary values, invalid input handled explicitly.
  • Tests considered: Happy path + edge cases have test cases (or at least documented expectations).
  • Types are explicit for any non-trivial data (inputs, outputs, intermediate structures).
  • No silent coercion; numbers parsed explicitly and validated when needed.
  • Map/Set used for lookups and deduplication; no unnecessary array scans.
  • Independent async operations use Promise.all or Promise.allSettled, not sequential awaits.
  • If mutating shared state in a branch, state is restored after the branch.
  • Helpers are small and named by intent; main flow is easy to follow.
  • Common perf traps avoided (regex in loops, repeated parsing, unnecessary cloning).

Following these practices keeps JS/TS code correct, fast, and easy to trust with minimal inspection.

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Automation

js-ts-business-crud-transforms

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

vercel-react-best-practices

React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.

Repository Source
23K212.7K
vercel
Coding

svelte5-best-practices

No summary provided by upstream source.

Repository SourceNeeds Review