Now that going bouldering once a week no longer counts as having a personality,
maybe I should try to stand out by being that guy who says “ECMAScript” instead
of “JavaScript”.
Anyway, here are some new(-ish) JavaScript features that I recently discovered.
groupBy (2024)
There are now built-in functions to group array elements. With
Object.groupBy(items) the result will be a normal object, and
with Map.groupBy(items) the result will be a Map.
Both of these have been widely available since March 2024, so they’re ready to
use.
Iterator helpers (2025)
I’ve always been irrationally upset that JavaScript’s iterators aren’t as
ergonomic as Rust’s. In Rust, you write something like
and that turns your original things into a lazy iterator, then does a bunch of
operations, and then collect() turns it back into a Vec. The iterators
just go through the items one by one instead of allocating a whole pointless
intermediate array.
If you do the same thing in JavaScript, it looks like
things .map((thing) => do_stuff(thing)) .filter((thing) => do_stuff2(thing)) .map((thing) => do_stuff3(thing));
Semantically that does the same thing as the Rust code, but each map and
filter allocates an intermediate array. If the initial things is large, this
could theoretically be a performance issue. So far, the way to optimise the
above code would be to just use for loops, but for loops are cringe, so the
fine folks at TC39 have blessed us with the same helper methods found on
Array, but for Iterators. So you can write
and bask in the glory of some beautiful premature optimisation.
These methods are available in all modern browsers as of 2025, so are also ready
to use.
Error.isError (2025/2026?)
If you ever get annoyed at catch (error: unknown) in TypeScript, there is now
a reliable way of checking whether something is an error, and that is
Error.isError(error). It’s more readable than
and it’s more reliable than error instanceof Error, because the instanceof
check apparently fails when the error was constructed in a different
realm.
Error.isError is available in all modern browsers, except that Safari will
currently return false for DOMException objects, which it shouldn’t. So this
one is almost ready to use.
TypedArray.prototype.subarray
This one’s actually been around for 10 years, but I only discovered it recently
(partly because I’ve never really used TypedArrays before). Unlike
TypedArray.prototype.slice, TypedArray.prototype.subarray
creates a new TypedArray that points to the same block of memory used by the
original, and the data isn’t copied. I found this useful when I was writing
(part of) a disassembler, which looked kind of like this:
function decode_instruction(data: Uint8Array): [Instruction, Uint8Array] { // Some horrifying ungodly logic return [ my_instruction, data.subarray(however_many_bytes_my_instruction_has_taken_up), ];}function* decode_instructions(data: Uint8Array): Generator<Instruction> { let rest = data; while (rest.length > 0) { const [instruction, new_rest] = decode_instruction(rest); yield instruction; rest = new_rest; }}
With TypedArray.prototype.slice, this would have allocated a whole new copy of
the input data after each instruction was decoded. Without subarray, I’d have
had to just keep a reference to the original array and pass around an offset
index, and that would have been so inconvenient.
When I discovered that Ghostty can change its colours based on the
system light/dark mode settings, I was curious whether I could achieve the same
effect in Neovim. It turns out, it’s possible! (You don’t need to use Ghostty
for this to work, that was just my inspiration to figure this out.)
This code only works on macOS, but I’m sure you could get an AI to translate it
to work on Windows or Linux:
function CheckAppearance() local theme = vim.fn.system('defaults read -g AppleInterfaceStyle'):gsub('\n', '') if theme == 'Dark' then vim.o.background = 'dark' vim.cmd('colorscheme modus_vivendi') vim.cmd [[ hi DiffText cterm=bold gui=bold ctermbg=225 guibg=DarkRed ]] -- For some reason the RenderMarkdown plugin doesn't pick up the changes -- when run through an autocmd vim.cmd [[ hi RenderMarkdown_bgtofg_RenderMarkdownCode guifg=#1e1e1e ]] else vim.o.background = 'light' -- These are my settings, but you can go wild and put whatever you want vim.cmd('colorscheme modus_operandi') vim.cmd [[ hi DiffText cterm=bold gui=bold ctermbg=225 guibg=LightRed ]] -- For some reason the RenderMarkdown plugin doesn't pick up the changes -- when run through an autocmd vim.cmd [[ hi RenderMarkdown_bgtofg_RenderMarkdownCode guifg=#f2f2f2 ]] endend-- Run on startupCheckAppearance()
So far, it’ll just check the appearance setting when you start Neovim. There
are a few ways to get it to dynamically detect changes to the setting:
Manually. Just create a user command and run it manually (ugh):
On focus gained. This is what I’m using – Neovim will check the appearance
whenever you activate it. This means it won’t switch immediately with the
system, but it’ll always match when you’re actually using Neovim:
Note: Some plugins might not pick up the changes properly, so you might have to
add extra stuff in the CheckAppearance function, like that
RenderMarkdown_bgtofg_RenderMarkdownCode highlight rule.
Our cat Jinx keeps getting sick
and vomiting, and the vets are saying this is most likely because young cats
like to eat random stuff outside. We really don’t want to restrict Jinx to being
an indoor cat, so we decided we would build him a little outside enclosure, a
“catio”.
Since this will be a big undertaking, we need to figure out what we want to
achieve with the catio, and make a plan. These are some hard requirements:
We need an enclosure that connects to the cat flap, so Jinx can get in and out
on his own.
The catio shouldn’t keep humans from using the back door to get into the
garden.
Apparently you need a permit to build anything higher than 2.5m, so that’s our
height limit.
Maintenance people still need to be able to access the pipes stuck to the
outside of the house.
And we also have some things that would be nice to have:
It would be great if there was no mesh right outside the bedroom window, and
if Jinx could get into the catio through the bedroom window.
The space should be as big as possible, so we can fit lots of toys and
climbing opportunities.
The frame shouldn’t be too tall on the neighbours’ side, so it doesn’t bother
them.
The basic plan we started out with looked like this:
We would then add mesh to the top, the slanted area, the right side (against the
brick wall), and the side facing the viewer. The walls of our flat should
ideally not get any mesh, so Jinx can get in and out through the windows.
Buuuuut there are a bunch of pipes running along the walls, so the frame needs
to somehow go around them.
My sister came up with the idea of putting the frame far enough away from the
walls to avoid the pipes, and then fill the gaps with flat wooden panels
(highlighted in red):
I discussed the plans with my dad (who had built us a tree-house when we were
young, so I figured he might know stuff). His advice was:
Build the frame out of 8×8cm wooden planks (we later haggled him down to
7×7cm, because we couldn’t find any 8×8cm planks).
Put 90° brackets in every right angle and hinges on non-90° angles.
Add diagonal stabilising struts to reinforce the corners.
If you know me in real life, you may be wondering: Didn’t I start this whole
project like 8 million years ago? Why did it take me so long to write this post?
Well, I’ve been sitting on this unfinished draft since June, planning to have
the following be the end of the article:
The final design looks like this:%% Better design %%In the next episode, we'll look at finding materials and starting to build stuff!
But it turned out, making a nice archviz-y render of the catio never felt quite
worth it. We used Blender to solve many of the design problems – and it was
really helpful for that purpose – but once we’d figured out the general idea,
it felt really tedious to painstakingly attach every single bracket and make
sure all the beams line up right, etc. Also, whenever I started finessing the
scene, I’d hit some new design issue that we needed to solve, and that would
mean more changes to the design, so, the more high-fidelity the model, the more
stuff I’d need to change every time.
The main takeaway for this episode is:
This is an exciting project.
It was absolutely the right choice to plan this project in 3D. I tried a few
sketches on paper and I kept getting hopelessly lost.
I’d wished for a nice photoreal render at the end, but this would have
required a lot more time investment. And if I’m gonna be spending time making
“art” in Blender, I’d rather it be spaceships instead of wooden frames with
mesh.
Anyway, here are some screenshots of the design as it currently exists:
I hesitate to call it “finished”, since a bunch of stuff might still change, and
we’ve probably already deviated from this plan anyway in real life.
Next time, we’ll look at the materials we got, and the beginning of the
construction process.
Elevators Lifts have this cool accessibility feature where, depending
on whether they’re going up or down, they’ll ding once or twice when they
arrive. This is so you know the direction without having to look at the display
above the doors.
I’ve finally come up with a mnemonic for remembering which is which:
One ding for going up – think “to one-up someone”
Two dings for going down – think “to double down on something”