Seven tips, one for each day of the week. Each one replaces a helper you used to write, a library you used to install, or a bug you used to ship. All of them work in current Node, Bun and evergreen browsers.

Monday: .at(-1)

const steps = ["parse", "check", "emit"]; steps.at(-1); // "emit" steps.at(-2); // "check" 

No more arr[arr.length - 1]. Works on strings too.

Tuesday: toSorted doesn't mutate

const nums = [10, 1, 2]; const sorted = nums.toSorted((a, b) => a - b); // [1, 2, 10] nums; // still [10, 1, 2] 

sort sorts in place and returns the same array, which surprises everyone once. toSorted, toReversed and toSpliced return a copy. And always pass a compare function: [1, 2, 10].sort() is [1, 10, 2], because the default sort compares strings.

Wednesday: structuredClone for deep copies

const original = { when: new Date(), tags: new Set(["a"]), nested: { n: 1 } }; const copy = structuredClone(original); copy.nested.n = 2; original.nested.n; // 1 copy.when instanceof Date; // true 

Unlike JSON.parse(JSON.stringify(x)), it keeps Dates, Maps and Sets. It throws on functions, which is a feature: you find out.

Thursday: Object.groupBy

const people = [{ name: "Ada", team: "core" }, { name: "Alan", team: "web" }, { name: "Grace", team: "core" }]; Object.groupBy(people, (p) => p.team); // { core: [Ada, Grace], web: [Alan] } 

The reduce you've written a hundred times, built in. (More in #9.)

Friday: AbortSignal.timeout

await fetch("https://api.github.com/zen", { signal: AbortSignal.timeout(3000) }); 

fetch has no timeout of its own. This one line gives it one, and rejects with a TimeoutError. (More in #10.)

Saturday: Error with cause

try { JSON.parse("{oops"); } catch (err) { throw new Error("Could not read settings", { cause: err }); } 

Your message says what you were doing, and err.cause still has the original error. (More in #7.)

Sunday: satisfies

const routes = { home: "/", docs: "/docs", } satisfies Record<string, `/${string}`>; routes.docs; // fine, and autocomplete knows the keys // routes.blog; // error: Property 'blog' does not exist 

It checks that every value starts with /, without widening the object to Record<string, …>, so you keep the exact keys. (More in #6.)

Trying them

Each of these is a two-line experiment, which is what a scratchpad is for. In JSLab, a JavaScript & TypeScript scratchpad for macOS, Auto Log puts the value of each top-level expression beside its line as you type. In Tuesday's block, nums; shows up as ▸ Array(3); expand it to check it's still 10, 1, 2.

If a result surprises you, right-click it and choose Explain Result to ask Ask AI why. It works with your own key, or Ollama locally.

And if you'd like the tips about JSLab itself: in your first week, it shows a short tip in the status bar the first time you meet a feature, and Help → Learning & Tips has every feature with a video.

JSLab launches Nov 15. Learning & Tips: https://js-lab.app/docs/learn