const x: Foo = { ... } as Foo | const x = { ... } satisfies Foo | Type-checked without assertion |
let resource = acquire(); try { ... } finally { release(resource) } | using resource = acquire() | Explicit resource disposal (TS 5.2+) |
if (x !== null && x !== undefined) | if (x != null) | Idiomatic null/undefined check |
arr.filter(x => x !== null) as T[] | arr.filter(x => x != null) | TS 5.5+ infers the type predicate — no cast; on older TS use an explicit (x): x is T predicate |
export { foo } from './foo/index.js' | Direct imports at call sites | Avoid barrel re-exports inside the package; barrel exports are for public APIs only |
import { readFile } from 'fs/promises' | import { readFile } from 'node:fs/promises' | node: protocol — unambiguous, lint-enforced in Biome |
async function f() { const a = await x(); const b = await y(); } | const [a, b] = await Promise.all([x(), y()]) | Parallel when independent |
value || fallback | value ?? fallback | || also swallows 0, '', and false — use ?? unless every falsy value really should take the fallback |
obj.x !== undefined ? obj.x : fallback | obj.x ?? fallback | Nullish coalescing — equivalent only when null should take the fallback too |
if (a) { if (b) { if (c) { ... } } } | Guard clauses with early returns | Reduce nesting |
try { risky() } catch (e: any) { ... } | try { risky() } catch (e) { ... } | Under strict the catch binding is already unknown; narrow with a type guard before use |
catch (err) { throw new Error('load failed') } | throw new Error('load failed', { cause: err }) | Preserve the cause chain |
[...arr].sort(cmp) / arr.slice().sort(cmp) | arr.toSorted(cmp) | Non-mutating array methods (ES2023) — also toReversed, toSpliced, with |
const c = new AbortController(); setTimeout(() => c.abort(), ms) | AbortSignal.timeout(ms) | Built-in timeout signal; combine with a caller's signal via AbortSignal.any([...]) |
JSON.parse(JSON.stringify(x)) | structuredClone(x) | Deep clone that preserves Date, Map, Set, and cycles |
enum Status { A, B, C } | const Status = { A: 'A', B: 'B', C: 'C' } as const | enum, namespace, and constructor parameter properties are non-erasable syntax rejected by TS 5.8 erasableSyntaxOnly and Node type-stripping — but switching numeric values to strings changes serialized output; keep values stable if they're persisted |
function f(a: string, b: string, c: string, d?: string) | function f(opts: FnOptions) | Options object when >3 params |
throw new Error('Bad input') (in a tool handler) | throw validationError('Bad input', { field: 'x' }) | Use framework error factories so the framework can classify and instrument |
const ATTR_KEY = 'mcp.tool.name' | import { ATTR_MCP_TOOL_NAME } from '@cyanheads/mcp-ts-core/utils' | Use framework attribute constants |