Skip to content

Instantly share code, notes, and snippets.

@bobvanderlinden
Last active May 23, 2024 07:42
Show Gist options
  • Save bobvanderlinden/4fe24822b51a300ba2d0b3d9d725735e to your computer and use it in GitHub Desktop.
Save bobvanderlinden/4fe24822b51a300ba2d0b3d9d725735e to your computer and use it in GitHub Desktop.
apply-ts-expect-error.js

For each tsc error, this adds a comment:

// TODO: Resolve type error
// @ts-expect-error
...

For each Unused '@ts-expect-error' directive error it removes the @ts-expect-error comment as well as any TODO: comment above.

Usage:

tsc --noEmit | node add-ts-expect-error.js
import { readFileSync, writeFileSync } from "fs";
/**
* Parses tsc stdout lines into error objects.
*/
function parseTsErrors(content) {
return [
...content.matchAll(
/^(?<file>[^\( ]+)\((?<lineNr>\d+),(?<columnNr>\d+)\): error TS(?<code>\d+): (?<description>.*)/gm,
),
].map(({ groups: { file, lineNr, columnNr, code, description } }) => ({
file,
lineNr: parseInt(lineNr, 10),
columnNr: parseInt(columnNr, 10),
code: parseInt(code, 10),
description,
}));
}
/**
* Deduplicates an array of items by a key function.
* Returns the first occurrence of each unique key.
*/
function uniqueBy(items, keyFn) {
return [...new Map(items.map((value) => [keyFn(value), value])).values()];
}
const content = readFileSync(0, "utf-8");
const fileErrors = Object.entries(
Object.groupBy(parseTsErrors(content), ({ file }) => file),
).map(([file, errors]) => [
file,
// Ignore multiple errors on the same line. Only pick the first one.
uniqueBy(errors, ({ file, lineNr }) => lineNr)
// Sort the errors by line number in descending order.
// Having them in descending order helps with making changes. The line numbers remain valid.
.toSorted((a, b) => b.lineNr - a.lineNr),
]);
for (const [file, errors] of fileErrors) {
const content = readFileSync(file, "utf-8");
const lines = content.split("\n");
for (const { lineNr, code } of errors) {
const lineIndex = lineNr - 1;
const line = lines[lineIndex];
const indentation = line.match(/^\s*/)[0];
function log(message) {
console.log(`${file}:${lineNr}: ${message}`);
}
if (code === 2578) {
// Remove @ts-expect-error for TS2578.
// TS2578: Unused '@ts-expect-error' directive.
// Sanity check.
if (!/^\s*\/\/\s*@ts-expect-error/.test(line)) {
log("Expected // @ts-expect-error directive");
continue;
}
// Also remove TODO comments above @ts-expect-error.
if (/^\s*\/\/\s*TODO:/.test(lines[lineIndex - 1])) {
log("Removed unused @ts-expect-error and TODO comment");
lines.splice(lineIndex - 1, 2);
} else {
log("Removed unused @ts-expect-error");
lines.splice(lineIndex, 1);
}
} else {
// Add @ts-expect-error for all other errors.
log("Added @ts-expect-error");
lines.splice(lineIndex, 0, `${indentation}// @ts-expect-error`);
lines.splice(lineIndex, 0, `${indentation}// TODO: Resolve type error`);
break;
}
}
const newContent = lines.join("\n");
writeFileSync(file, newContent, "utf-8");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment