jbscript.dev

Home

TILs from Advent of Code 2023

Small things I’ve learnt while doing Advent of Code for the first time, updated as I work through this year’s problems.

Table of contents

Fixing “Cannot-redeclare block scoped variable” when using TypeScript to check your JavaScript in VS Code

With a jsconfig.json file in the root of your project and JSDoc in your code, VS Code will use its internal version of TypeScript to check your JavaScript, giving you 95% of the benefit of TypeScript without having to set up any tooling:

{
  "compilerOptions": {
    "checkJs": true,
    "target": "ES2022"
  }
}

However, if a .js file doesn’t import or export anything, TypeScript will assume it’s a global script rather than a module, and will complain if any two such files declare a top-level variable with the same name.

To fix this, add "moduleDetection": "force" to force every .js file to be treated as a module:

{
  "compilerOptions": {
    "checkJs": true,
    "target": "ES2022",
    "moduleDetection": "force"
  }
}

String.prototype.matchAll() exists and is less clunky than RegExp.prototype.exec()

Either I haven’t been doing enough string parsing lately, or I’ve been avoiding using it because the main application I work on at work only ditched support for Internet Explorer 11 relatively recently, but String.prototype.matchAll() is a less clunky way to get multiple match details for a regular expression against a given string:

Before:

let match
while ((match = schematicRegExp.exec(line))) {
  // ...
}

After:

for (let match of line.matchAll(schematicRegExp)) {
  // ...
}

Set or increment a number in a JavaScript object/array

My initial code in one of the problems was something like:

if (!counts[index]) {
  counts[index] = increaseBy
} else {
  counts[index] += increaseBy
}

You can use the nullish coalescing operator (??) to make this operation more compact, as counts[index] will be undefined if it’s never been set:

counts[index] = (counts[index] ?? 0) + increaseBy

Create an array with a number of objects

Array.from() takes a second mapping function argument:

// Create an array containing 256 empty objects
let boxes = Array.from(Array(256), () => ({}))

This is less foot-gunny than using Array.prototype.fill(), which will bite you if you fill an array with something you later need to mutate.

Automatically re-running Node.js every time you save

I’d just got a basic version of this working using fs.watch() and child_process.spawn() when—🤦—I learnt Node.js has a built-in --watch flag, which also does nice things like restarting if another module the script imports is modified.

This can be handy while working on initial solutions against the example data, especially if you’re logging outputs the puzzle provides for example data in the same format, like I tend to do:

node --watch index.js test
created | updated