JavaScript
Clarity is the highest JavaScript virtue. If your code requires a comment to explain its control flow, rewrite it.
JavaScript rewards explicit, readable code. Prefer boring patterns that are easy to understand over clever tricks that
save characters.
References
- Functions, closures, composition → [
${CLAUDE_SKILL_DIR}/references/functions.md] — Arrow function examples,
closure patterns, early return, parameter destructuring
- Async patterns, error handling, concurrency → [
${CLAUDE_SKILL_DIR}/references/async.md] — Promise.all/race/any
examples, cancellation, custom error classes, for-await
- Objects, arrays, iteration, Map/Set → [
${CLAUDE_SKILL_DIR}/references/objects-and-arrays.md] — Iteration
decision table, destructuring patterns, immutable updates, generators
- ES modules, imports, barrel files → [
${CLAUDE_SKILL_DIR}/references/modules.md] — Import ordering, barrel file
rationale, directory import pitfalls, dynamic imports
- JSDoc typing, full tag catalog → [
${CLAUDE_SKILL_DIR}/references/jsdoc.md] — Full tag reference (@callback,
@template, @enum), type assertions, class modifiers
- General JS idioms and edge cases → [
${CLAUDE_SKILL_DIR}/references/idioms.md] — Variable/naming examples,
equality coercion table, modern syntax patterns
Variables and Declarations
const by default. Use let only when reassignment is required. Never var.
const prevents reassignment, not mutation. Objects and arrays declared with const can still be mutated.
- Block scope only.
let/const are block-scoped; var is function-scoped and hoists — this causes bugs in loops
and conditionals.
- One declaration per line. Never chain
const a = 1, b = 2.
- Group declarations.
const first, then let.
Naming
| Entity |
Style |
Examples |
| Variables, functions |
camelCase |
userName, fetchData |
| Classes, constructors |
PascalCase |
UserService, HttpClient |
| True compile-time constants |
SCREAMING_SNAKE_CASE |
MAX_RETRIES, API_BASE_URL |
| Private fields/methods |
# prefix (class) |
#count, #validate() |
| Booleans |
is/has/can/should prefix |
isValid, hasAccess |
| File names |
kebab-case or camelCase |
user-service.js, userService.js |
- SCREAMING_SNAKE_CASE is for true constants only — values known at compile time, never computed at runtime. A
variable holding a function return value uses camelCase.
- Descriptive names.
userCount not n. Short names (i, x) only in tiny scopes (loop indices, simple arrow
callbacks).
- Accepted abbreviations:
url, id, err, ctx, req, res — universally understood. Avoid all others.
- No redundant context.
car.make not car.carMake.
- Consistent vocabulary. Use the same word for the same concept throughout a codebase —
getUser() everywhere, not
getUserInfo() / getClientData() / getCustomerRecord().
Equality and Safety
- Always
=== and !==. Never == except for value == null (checks both null and undefined).
?? over || for defaults — || treats 0, "", false as falsy.
?. for optional access. Don't overuse — missing data you expect should throw, not silently return undefined.
- Know the falsy values:
false, 0, -0, 0n, "", null, undefined, NaN. Everything else is truthy,
including [], {}, and "0".
Ternary Operator
One-liners or split-per-branch only. Ternaries are acceptable in two forms:
// OK — fits on one line
const label = isActive ? 'Active' : 'Inactive';
// OK — each branch on its own line
const label = isActive
? buildActiveLabel(user)
: buildInactiveLabel(user);
Any ternary that doesn't fit one of these two patterns must be rewritten as if/else or early return.
Nested ternaries are banned. No exceptions. Use if/else, early returns, or a lookup object.
Modern Syntax
- Template literals for string interpolation:
`Hello, ${name}`. Don't use template literals for strings
without interpolation — use plain quotes.
- Spread for copies:
{ ...obj } and [...arr]. Never Object.assign.
- Rest parameters to collect remaining:
const { id, ...rest } = user.
- Shorthand properties:
{ name, age } not { name: name, age: age }. Group shorthand properties at the top of
object literals.
- Computed property names:
{ [key]: value, [${key}Date]: new Date() }.
- Logical assignment operators:
opts.timeout ??= 5000 (assign if nullish), opts.name ||= "default" (assign if
falsy), opts.handler &&= wrap(opts.handler) (assign if truthy).
Functions
- Arrow functions for callbacks and anonymous functions. Use function declarations only when hoisting or
this
binding is needed.
- Prefer parentheses around arrow function parameters even for single params — smaller diffs when adding/removing
parameters.
- Implicit return for single expressions (no braces). Explicit return (braces) for multi-statement bodies.
- Arrow functions capture lexical
this — they do NOT have their own this. Never use arrow functions as object
methods or on prototypes.
- Destructured options for 3+ parameters. Self-documenting and order-independent.
- Default parameters over
|| or manual checks. Defaults are evaluated left-to-right and can reference earlier
params.
- Rest parameters over
arguments object. arguments is array-like, not a real Array.
- Early return. Guard clauses first, happy path flat. Reduce nesting.
- One function, one job. If the name contains "and", split it.
- Keep functions under ~30 lines. Extract helpers. Use composition over complex branching.
- Prefer pure functions (same input = same output, no side effects). Isolate side effects (DOM, network, logging) —
don't hide them inside data transformations.
- Closures retain references to outer variables, not copies. Be cautious with large objects captured unintentionally
— they won't be garbage collected until the closure is released.
Async
async/await over .then() chains for sequential operations.
- Always
await promises. Missing await = floating promise = silent failures.
return await only inside try blocks where you need to catch the awaited error. Otherwise just return promise
— no need for async wrapper.
Promise.all for independent parallel work. Rejects on first rejection.
Promise.allSettled when all results matter regardless of individual failures.
Promise.race for timeouts. Promise.any for fallbacks (rejects only when ALL reject).
- Avoid sequential awaits in loops. Use
Promise.all(items.map(...)) for parallel. Use a concurrency limiter (e.g.
p-map) for large arrays.
- Throw
Error objects, never strings or plain objects — strings lose stack traces.
- Custom error classes when callers need to distinguish errors: extend
Error, set this.name, add context
properties.
- Never swallow errors. Every
catch must handle, rethrow, or report. Empty catch blocks hide bugs.
console.log(err) alone is not handling.
- Let errors propagate to a top-level handler when possible. Don't wrap every
await in try/catch — only where
you need to handle at that level.
- Attach
.catch() to non-awaited promise chains. Unhandled rejections crash Node.js. Fire-and-forget:
fetchData().catch(reportError).
- Only use
new Promise() to wrap callback-based APIs. Most async code should compose existing promises with
async/await.
AbortController for cancellable async operations: pass { signal } to fetch and other APIs.
for await...of for async iterables (streams, async generators).
Modules
- ES modules only.
import/export for all new code. CommonJS (require) is legacy — use only when runtime
requires it.
- Named exports over default exports. Default exports cause inconsistent naming across importers. Exception: default
exports acceptable when required by framework convention (Next.js pages, Remix routes).
- Don't export mutable
let bindings. Export accessor functions instead: export function getCount() not
export let count.
- Imports at the top, grouped with blank lines: built-in (
node:fs), external (express), internal (./utils).
- Always include file extensions in import paths —
"./user.js", not "./user". Extensionless imports vary across
runtimes.
- No directory imports. Import from the file directly, not from a folder that resolves to
index.js.
- No barrel files in subdirectories.
index.js re-exports create indirection and hurt tree-shaking. Acceptable only
as a standalone package entry point where the runtime can enforce the boundary via package.json exports.
- No circular dependencies. Extract shared code to a third module, merge tightly coupled modules, or use dependency
injection.
- No wildcard re-exports. Explicit re-exports only — wildcards bypass tree-shaking.
- Merge imports from the same module into a single statement.
- Namespace imports (
import * as dateFns) for large modules (5+ items). Prefer named imports when importing fewer
than ~5 items.
- Dynamic imports (
import()) for code splitting and lazy loading: routes loaded on navigation, large conditional
dependencies, feature flags.
- One concern per module. If a module exports unrelated functionality, split it.
- Side-effect imports (
import "./polyfill.js") should be rare. Document why.
Objects and Arrays
- Literal syntax.
{} and [], never new Object() / new Array().
- Use method shorthand on objects:
greet() { } not greet: function() { }.
- Spread for copies.
{ ...obj } and [...arr]. Prefer over Object.assign.
- Destructure to extract properties. Prefer parameter destructuring.
- Dot notation for static properties, brackets for dynamic:
user.name vs user[dynamicKey].
Object.hasOwn(obj, key) instead of obj.hasOwnProperty(key).
- Functional array methods (
map, filter, find, some, every, flatMap, reduce) over imperative loops for
data transformation.
- Always return in
map, filter, reduce callbacks.
Array.from(arrayLike) for array-like objects (not spread). Array.from(iterable, mapFn) instead of
[...iterable].map(mapFn) — avoids intermediate array.
- Don't mutate inputs. Return new objects/arrays. Immutable update patterns: add
[...arr, item], remove
arr.filter(...), update arr.map(...).
for...of for side-effect loops. Never for...in on arrays.
- Return objects for multiple values, not arrays — callers don't depend on order.
- Never extend built-in prototypes (
Array.prototype, Object.prototype). Use utility functions or subclasses.
Prefer for...of for side-effect loops, Array.prototype methods (.map, .filter, .reduce, .find, .some,
.every) for data transforms, for for index-needed loops. See ${CLAUDE_SKILL_DIR}/references/objects-and-arrays.md
for the full iteration decision table.
Use Map when keys aren't strings or are user-provided (avoids prototype pollution). Use Set for dedup
([...new Set(items)]). Use generators (function*) for lazy sequences and deferred computation.
Classes
- ES
class syntax only. No function constructors or prototype manipulation.
#private fields for encapsulation. Not _ convention.
- Composition over inheritance. Use
extends only for true "is-a" relationships.
- No empty constructors. If the constructor only calls
super(), omit it.
- Static methods for operations that don't need instance state.
- Methods can return
this for fluent/chainable APIs.
- Don't force classes when plain functions and objects suffice. A class with one method is a function in disguise.
JSDoc Typing
For pure JavaScript projects that don't use TypeScript, use JSDoc annotations to provide type safety through editor
tooling. Enable // @ts-check at file top or checkJs in jsconfig.json.
Core tags: @type, @param, @returns, @typedef (with @property), @template. See
${CLAUDE_SKILL_DIR}/references/jsdoc.md for the full tag catalog including @callback, @enum, class modifiers, and
type import syntax.
JSDoc Best Practices
- Annotate public API boundaries — exported functions, classes, module-level variables. Internal code often needs
fewer annotations; types flow from context.
- Prefer inline TypeScript syntax in JSDoc types:
{string | number} over {(string|number)}.
- Use
@typedef for shared shapes — define once near file top or in types.js.
- Don't annotate the obvious — if
const x = 5 is clearly a number, skip @type.
Application
When writing JavaScript code:
- Apply all conventions silently — don't narrate each rule being followed.
- If an existing codebase contradicts a convention, follow the codebase and flag the divergence once.
When reviewing JavaScript code:
- Cite the specific violation and show the fix inline.
- Don't lecture or quote the rule — state what's wrong and how to fix it.
Bad review comment:
"According to best practices, you should use const instead of let
when the variable is never reassigned."
Good review comment:
"`let` -> `const` — `config` is never reassigned."
Code Navigation — LSP Required
A typescript-language-server LSP server is configured for all JS/TS file types (.js, .jsx, .ts, .tsx, .mjs,
.cjs, .mts, .cts). Always use LSP tools for code navigation instead of Grep or Glob. LSP understands module
resolution, type inference, scope rules, and project boundaries — text search does not.
Tool Routing
- Find where a function/class/variable is defined →
goToDefinition — Resolves imports, re-exports, aliases
- Find all usages of a symbol →
findReferences — Scope-aware, no false positives from string matches
- Get type signature, docs, or return types →
hover — Instant type info without reading source files
- List all exports/symbols in a file →
documentSymbol — Structured output vs grepping for
function/class/export
- Find a symbol by name across the project →
workspaceSymbol — Searches all modules
- Find implementations of an interface →
goToImplementation — Knows the type system
- Find what calls a function →
incomingCalls — Precise call graph across module boundaries
- Find what a function calls →
outgoingCalls — Structured dependency map
Grep/Glob remain appropriate for: text in comments, string literals, log messages, TODO markers, config values, env
vars, CSS classes, file name patterns, URLs, error message text.
When spawning subagents for JS/TS codebase exploration, instruct them to use LSP tools. Subagents have access to the
same LSP server.
Integration
The coding skill governs workflow; this skill governs JavaScript implementation choices. For TypeScript projects,
the typescript skill extends this one.
Converted and distributed by TomeVault — claim your Tome and manage your conversions.
1---2name: xobotyi-cc-foundry-javascript3description: JavaScript4---56# JavaScript78**Clarity is the highest JavaScript virtue. If your code requires a comment to explain its control flow, rewrite it.**910JavaScript rewards explicit, readable code. Prefer boring patterns that are easy to understand over clever tricks that11save characters.1213## References1415- **Functions, closures, composition** → [`${CLAUDE_SKILL_DIR}/references/functions.md`] — Arrow function examples,16 closure patterns, early return, parameter destructuring17- **Async patterns, error handling, concurrency** → [`${CLAUDE_SKILL_DIR}/references/async.md`] — Promise.all/race/any18 examples, cancellation, custom error classes, for-await19- **Objects, arrays, iteration, Map/Set** → [`${CLAUDE_SKILL_DIR}/references/objects-and-arrays.md`] — Iteration20 decision table, destructuring patterns, immutable updates, generators21- **ES modules, imports, barrel files** → [`${CLAUDE_SKILL_DIR}/references/modules.md`] — Import ordering, barrel file22 rationale, directory import pitfalls, dynamic imports23- **JSDoc typing, full tag catalog** → [`${CLAUDE_SKILL_DIR}/references/jsdoc.md`] — Full tag reference (@callback,24 @template, @enum), type assertions, class modifiers25- **General JS idioms and edge cases** → [`${CLAUDE_SKILL_DIR}/references/idioms.md`] — Variable/naming examples,26 equality coercion table, modern syntax patterns2728## Variables and Declarations2930- **`const` by default.** Use `let` only when reassignment is required. Never `var`.31- **`const` prevents reassignment, not mutation.** Objects and arrays declared with `const` can still be mutated.32- **Block scope only.** `let`/`const` are block-scoped; `var` is function-scoped and hoists — this causes bugs in loops33 and conditionals.34- **One declaration per line.** Never chain `const a = 1, b = 2`.35- **Group declarations.** `const` first, then `let`.3637## Naming3839| Entity | Style | Examples |40| --------------------------- | -------------------------------- | ----------------------------------- |41| Variables, functions | camelCase | `userName`, `fetchData` |42| Classes, constructors | PascalCase | `UserService`, `HttpClient` |43| True compile-time constants | SCREAMING_SNAKE_CASE | `MAX_RETRIES`, `API_BASE_URL` |44| Private fields/methods | `#` prefix (class) | `#count`, `#validate()` |45| Booleans | `is`/`has`/`can`/`should` prefix | `isValid`, `hasAccess` |46| File names | kebab-case or camelCase | `user-service.js`, `userService.js` |4748- **SCREAMING_SNAKE_CASE is for true constants only** — values known at compile time, never computed at runtime. A49 variable holding a function return value uses camelCase.50- **Descriptive names.** `userCount` not `n`. Short names (`i`, `x`) only in tiny scopes (loop indices, simple arrow51 callbacks).52- **Accepted abbreviations:** `url`, `id`, `err`, `ctx`, `req`, `res` — universally understood. Avoid all others.53- **No redundant context.** `car.make` not `car.carMake`.54- **Consistent vocabulary.** Use the same word for the same concept throughout a codebase — `getUser()` everywhere, not55 `getUserInfo()` / `getClientData()` / `getCustomerRecord()`.5657## Equality and Safety5859- **Always `===` and `!==`.** Never `==` except for `value == null` (checks both `null` and `undefined`).60- **`??` over `||`** for defaults — `||` treats `0`, `""`, `false` as falsy.61- **`?.` for optional access.** Don't overuse — missing data you expect should throw, not silently return `undefined`.62- **Know the falsy values:** `false`, `0`, `-0`, `0n`, `""`, `null`, `undefined`, `NaN`. Everything else is truthy,63 including `[]`, `{}`, and `"0"`.6465## Ternary Operator6667- **One-liners or split-per-branch only.** Ternaries are acceptable in two forms:6869 ```js70 // OK — fits on one line71 const label = isActive ? 'Active' : 'Inactive';7273 // OK — each branch on its own line74 const label = isActive75 ? buildActiveLabel(user)76 : buildInactiveLabel(user);77 ```7879 Any ternary that doesn't fit one of these two patterns must be rewritten as `if`/`else` or early return.8081- **Nested ternaries are banned.** No exceptions. Use `if`/`else`, early returns, or a lookup object.8283## Modern Syntax8485- **Template literals** for string interpolation: `` `Hello, ${name}` ``. Don't use template literals for strings86 without interpolation — use plain quotes.87- **Spread for copies:** `{ ...obj }` and `[...arr]`. Never `Object.assign`.88- **Rest parameters** to collect remaining: `const { id, ...rest } = user`.89- **Shorthand properties:** `{ name, age }` not `{ name: name, age: age }`. Group shorthand properties at the top of90 object literals.91- **Computed property names:** `{ [key]: value, [`${key}Date`]: new Date() }`.92- **Logical assignment operators:** `opts.timeout ??= 5000` (assign if nullish), `opts.name ||= "default"` (assign if93 falsy), `opts.handler &&= wrap(opts.handler)` (assign if truthy).9495## Functions9697- **Arrow functions** for callbacks and anonymous functions. Use function declarations only when hoisting or `this`98 binding is needed.99- **Prefer parentheses** around arrow function parameters even for single params — smaller diffs when adding/removing100 parameters.101- **Implicit return** for single expressions (no braces). Explicit return (braces) for multi-statement bodies.102- **Arrow functions capture lexical `this`** — they do NOT have their own `this`. Never use arrow functions as object103 methods or on prototypes.104- **Destructured options** for 3+ parameters. Self-documenting and order-independent.105- **Default parameters** over `||` or manual checks. Defaults are evaluated left-to-right and can reference earlier106 params.107- **Rest parameters** over `arguments` object. `arguments` is array-like, not a real Array.108- **Early return.** Guard clauses first, happy path flat. Reduce nesting.109- **One function, one job.** If the name contains "and", split it.110- **Keep functions under ~30 lines.** Extract helpers. Use composition over complex branching.111- **Prefer pure functions** (same input = same output, no side effects). Isolate side effects (DOM, network, logging) —112 don't hide them inside data transformations.113- **Closures retain references to outer variables, not copies.** Be cautious with large objects captured unintentionally114 — they won't be garbage collected until the closure is released.115116## Async117118- **`async`/`await` over `.then()` chains** for sequential operations.119- **Always `await` promises.** Missing `await` = floating promise = silent failures.120- **`return await` only inside `try` blocks** where you need to catch the awaited error. Otherwise just `return promise`121 — no need for `async` wrapper.122- **`Promise.all`** for independent parallel work. Rejects on first rejection.123- **`Promise.allSettled`** when all results matter regardless of individual failures.124- **`Promise.race`** for timeouts. **`Promise.any`** for fallbacks (rejects only when ALL reject).125- **Avoid sequential awaits in loops.** Use `Promise.all(items.map(...))` for parallel. Use a concurrency limiter (e.g.126 `p-map`) for large arrays.127- **Throw `Error` objects**, never strings or plain objects — strings lose stack traces.128- **Custom error classes** when callers need to distinguish errors: extend `Error`, set `this.name`, add context129 properties.130- **Never swallow errors.** Every `catch` must handle, rethrow, or report. Empty `catch` blocks hide bugs.131 `console.log(err)` alone is not handling.132- **Let errors propagate** to a top-level handler when possible. Don't wrap every `await` in `try`/`catch` — only where133 you need to handle at that level.134- **Attach `.catch()` to non-awaited promise chains.** Unhandled rejections crash Node.js. Fire-and-forget:135 `fetchData().catch(reportError)`.136- **Only use `new Promise()`** to wrap callback-based APIs. Most async code should compose existing promises with137 `async`/`await`.138- **`AbortController`** for cancellable async operations: pass `{ signal }` to `fetch` and other APIs.139- **`for await...of`** for async iterables (streams, async generators).140141## Modules142143- **ES modules only.** `import`/`export` for all new code. CommonJS (`require`) is legacy — use only when runtime144 requires it.145- **Named exports** over default exports. Default exports cause inconsistent naming across importers. Exception: default146 exports acceptable when required by framework convention (Next.js pages, Remix routes).147- **Don't export mutable `let` bindings.** Export accessor functions instead: `export function getCount()` not148 `export let count`.149- **Imports at the top**, grouped with blank lines: built-in (`node:fs`), external (`express`), internal (`./utils`).150- **Always include file extensions** in import paths — `"./user.js"`, not `"./user"`. Extensionless imports vary across151 runtimes.152- **No directory imports.** Import from the file directly, not from a folder that resolves to `index.js`.153- **No barrel files in subdirectories.** `index.js` re-exports create indirection and hurt tree-shaking. Acceptable only154 as a standalone package entry point where the runtime can enforce the boundary via `package.json` `exports`.155- **No circular dependencies.** Extract shared code to a third module, merge tightly coupled modules, or use dependency156 injection.157- **No wildcard re-exports.** Explicit re-exports only — wildcards bypass tree-shaking.158- **Merge imports from the same module** into a single statement.159- **Namespace imports** (`import * as dateFns`) for large modules (5+ items). Prefer named imports when importing fewer160 than ~5 items.161- **Dynamic imports** (`import()`) for code splitting and lazy loading: routes loaded on navigation, large conditional162 dependencies, feature flags.163- **One concern per module.** If a module exports unrelated functionality, split it.164- **Side-effect imports** (`import "./polyfill.js"`) should be rare. Document why.165166## Objects and Arrays167168- **Literal syntax.** `{}` and `[]`, never `new Object()` / `new Array()`.169- **Use method shorthand** on objects: `greet() { }` not `greet: function() { }`.170- **Spread for copies.** `{ ...obj }` and `[...arr]`. Prefer over `Object.assign`.171- **Destructure** to extract properties. Prefer parameter destructuring.172- **Dot notation** for static properties, brackets for dynamic: `user.name` vs `user[dynamicKey]`.173- **`Object.hasOwn(obj, key)`** instead of `obj.hasOwnProperty(key)`.174- **Functional array methods** (`map`, `filter`, `find`, `some`, `every`, `flatMap`, `reduce`) over imperative loops for175 data transformation.176- **Always return** in `map`, `filter`, `reduce` callbacks.177- **`Array.from(arrayLike)`** for array-like objects (not spread). `Array.from(iterable, mapFn)` instead of178 `[...iterable].map(mapFn)` — avoids intermediate array.179- **Don't mutate inputs.** Return new objects/arrays. Immutable update patterns: add `[...arr, item]`, remove180 `arr.filter(...)`, update `arr.map(...)`.181- **`for...of`** for side-effect loops. Never `for...in` on arrays.182- **Return objects for multiple values**, not arrays — callers don't depend on order.183- **Never extend built-in prototypes** (`Array.prototype`, `Object.prototype`). Use utility functions or subclasses.184185Prefer `for...of` for side-effect loops, `Array.prototype` methods (`.map`, `.filter`, `.reduce`, `.find`, `.some`,186`.every`) for data transforms, `for` for index-needed loops. See `${CLAUDE_SKILL_DIR}/references/objects-and-arrays.md`187for the full iteration decision table.188189Use `Map` when keys aren't strings or are user-provided (avoids prototype pollution). Use `Set` for dedup190(`[...new Set(items)]`). Use generators (`function*`) for lazy sequences and deferred computation.191192## Classes193194- **ES `class` syntax** only. No function constructors or prototype manipulation.195- **`#private` fields** for encapsulation. Not `_` convention.196- **Composition over inheritance.** Use `extends` only for true "is-a" relationships.197- **No empty constructors.** If the constructor only calls `super()`, omit it.198- **Static methods** for operations that don't need instance state.199- **Methods can return `this`** for fluent/chainable APIs.200- **Don't force classes** when plain functions and objects suffice. A class with one method is a function in disguise.201202## JSDoc Typing203204For pure JavaScript projects that don't use TypeScript, use JSDoc annotations to provide type safety through editor205tooling. Enable `// @ts-check` at file top or `checkJs` in `jsconfig.json`.206207Core tags: `@type`, `@param`, `@returns`, `@typedef` (with `@property`), `@template`. See208`${CLAUDE_SKILL_DIR}/references/jsdoc.md` for the full tag catalog including `@callback`, `@enum`, class modifiers, and209type import syntax.210211### JSDoc Best Practices212213- **Annotate public API boundaries** — exported functions, classes, module-level variables. Internal code often needs214 fewer annotations; types flow from context.215- **Prefer inline TypeScript syntax** in JSDoc types: `{string | number}` over `{(string|number)}`.216- **Use `@typedef` for shared shapes** — define once near file top or in `types.js`.217- **Don't annotate the obvious** — if `const x = 5` is clearly a number, skip `@type`.218219## Application220221When **writing** JavaScript code:222223- Apply all conventions silently — don't narrate each rule being followed.224- If an existing codebase contradicts a convention, follow the codebase and flag the divergence once.225226When **reviewing** JavaScript code:227228- Cite the specific violation and show the fix inline.229- Don't lecture or quote the rule — state what's wrong and how to fix it.230231```232Bad review comment:233 "According to best practices, you should use const instead of let234 when the variable is never reassigned."235236Good review comment:237 "`let` -> `const` — `config` is never reassigned."238```239240## Code Navigation — LSP Required241242A `typescript-language-server` LSP server is configured for all JS/TS file types (`.js`, `.jsx`, `.ts`, `.tsx`, `.mjs`,243`.cjs`, `.mts`, `.cts`). **Always use LSP tools for code navigation instead of Grep or Glob.** LSP understands module244resolution, type inference, scope rules, and project boundaries — text search does not.245246### Tool Routing247248- **Find where a function/class/variable is defined** → `goToDefinition` — Resolves imports, re-exports, aliases249- **Find all usages of a symbol** → `findReferences` — Scope-aware, no false positives from string matches250- **Get type signature, docs, or return types** → `hover` — Instant type info without reading source files251- **List all exports/symbols in a file** → `documentSymbol` — Structured output vs grepping for252 `function`/`class`/`export`253- **Find a symbol by name across the project** → `workspaceSymbol` — Searches all modules254- **Find implementations of an interface** → `goToImplementation` — Knows the type system255- **Find what calls a function** → `incomingCalls` — Precise call graph across module boundaries256- **Find what a function calls** → `outgoingCalls` — Structured dependency map257258**Grep/Glob remain appropriate for:** text in comments, string literals, log messages, TODO markers, config values, env259vars, CSS classes, file name patterns, URLs, error message text.260261When spawning subagents for JS/TS codebase exploration, instruct them to use LSP tools. Subagents have access to the262same LSP server.263264## Integration265266The **coding** skill governs workflow; this skill governs JavaScript implementation choices. For TypeScript projects,267the **typescript** skill extends this one.268269---270> Converted and distributed by [TomeVault](https://tomevault.io/claim/xobotyi) — claim your Tome and manage your conversions.271<!-- tomevault:4.0:skill_md:2026-04-13 -->