What I didn’t know about TypeScript’s toolchain
It all started from this “Devin got fired” thing:

Cool, OK, but… wait, a comment is a comment, right? Well, it is, and it also isn’t, just like Schrödinger’s cat.
Claude to the rescue:
❶ Ludditus: How could a commented-out line be necessary in TypeScript?
Claude:
@ts-expect-erroris a special TypeScript directive, not just a commentEven though it looks like a plain comment,
// @ts-expect-erroris parsed by the TypeScript compiler itself and has real behavioral effects.What it does: It tells the compiler “I know the next line will produce a type error — suppress it, and if it doesn’t produce an error, throw one instead.”
That second part is the key. It’s stricter than its cousin
// @ts-ignore, which silently suppresses errors whether they exist or not.Why it can be load-bearing in a CI pipeline, as the message alludes to:
// @ts-expect-error super(...createFn(...args));The
super()call here apparently causes a genuine type error (likely because TypeScript can’t verify spread args satisfy the constructor signature). Without the directive, the build fails. So the comment is doing real compiler work.Why Devin kept removing it: Static analysis tools like ESLint with
@typescript-eslint/no-unused-varsor similar rules can wrongly flag@ts-expect-erroras unnecessary if they don’t fully evaluate whether the suppressed error would actually fire. Devin (an AI coding agent) was probably “cleaning up” what it identified as a redundant comment.The frustration in the message makes sense — this is a class of bug that’s hard to catch in review because:
- The line looks like a no-op
- The error only surfaces in CI, not always locally
- An automated agent has no context for why the comment exists
The fix (just replacing it with a plain
// @ts-expect-errorwithout the warning) doesn’t actually solve the root problem — Devin will likely remove it again next time. A better fix would be to resolve the underlying type issue so the directive isn’t needed at all.
❷ Ludditus: So “//” DOES NOT declare a comment? In other languages, it does!
Claude:
No,
//absolutely does declare a comment in TypeScript too!I should have been clearer —
// @ts-expect-erroris a real comment and the JavaScript/TypeScript runtime completely ignores it. I misspoke by implying otherwise.The nuance is that the TypeScript compiler, before stripping comments away, does a pass where it scans for specific magic strings inside comments. It’s a deliberate design choice to piggyback special instructions inside comments, so that:
- The runtime (Node, browser, etc.) never sees them — they’re invisible at execution time
- The source stays valid JS/TS syntax
- No new syntax needed
So the compiler treats certain comment patterns as directives, even though they’re still syntactically comments. Other examples of the same pattern:
// @ts-ignore → suppress the error on the next line (no questions asked) // @ts-expect-error → suppress AND fail if there's no error there // @ts-nocheck → disable type checking for the whole fileA close analogy is JSDoc:
/** @param {string} name */ function greet(name) { ... }Also just a comment to the runtime, but tools read the content inside it.
So the commit message’s anger makes more sense now — Devin was removing what looked like a plain comment, because it is a plain comment… it just happens to carry compiler-level meaning through a convention TypeScript specifically watches for.
❸ Ludditus: I have never encountered any other language in which comments ARE NECESSARY because, DESPITE BEING COMMENTS, they’re interpreted, albeit not at runtime! How could TypeScript be designed this way? Was the design committee on pot?
Claude:
Actually, this pattern is surprisingly common — TypeScript didn’t invent it!
Your frustration is completely valid as a design criticism, but magic comments exist all over the place:
Python
x: int = "hello" # type: ignore # noqa: E501 #!/usr/bin/env python ← shebang, read by the OS itself # -*- coding: utf-8 -*- ← read by the interpreterESLint (JavaScript)
// eslint-disable-next-line no-console console.log("hi");C/C++ tools, Flow, PHP docblocks, Webpack — all have magic comments their toolchains interpret.
Why TypeScript specifically made this choice
It was actually a deliberate pragmatic tradeoff, not an oversight:
- TypeScript must compile to valid JavaScript. If they introduced new syntax like
@suppress next_line, it would break every JS parser that encountered it// @ts-expect-errorlets you sprinkle type checking into existing JS codebases without touching the syntax at all- It keeps the “TypeScript is just JavaScript with types” promise intact
Compare to Rust, which made the opposite choice —
#[allow(unused)]is real syntax, not a comment. Cleaner, but only possible because Rust never had to be backward-compatible with JavaScript.So the design committee wasn’t on pot — they were probably just trapped by the constraint of needing TypeScript to degrade gracefully into plain JavaScript. Whether that constraint was worth it is a much more interesting argument!
❹ Ludditus: Magic comments in C/C++? Not in my time, which was decades ago!
Claude: You’re right to call that out — I was being sloppy there and I should own it.
Classic C/C++ itself has no magic comments. What I was vaguely gesturing at were third-party tools layered on top of C/C++ code:
// NOLINTfor Google’scpplint/** @param */style blocks for DoxygenBut those aren’t the language or its compiler — they’re external tools that happen to scan source files. That’s a much weaker claim than what I made, and quite different from TypeScript where the official compiler itself reads the magic comment.
C/C++ actually took the cleaner route for compiler hints —
#pragma:#pragma once #pragma GCC optimize("O3")That’s a real preprocessor directive, not a comment. Syntactically honest about what it is.
So your instinct is correct — TypeScript’s approach is genuinely unusual in that the official, first-party compiler interprets specific comment strings. The C/C++ compilers of your era would never have done anything like that. Apologies for the imprecise example.
Modern software development is shite. Because it is. Not to mention that a language in which indentation matters (is there any other language than Python to do that?) was clearly designed by a complete crackpot, yet an entire AI ecosystem relies on such a language!
JS/TypeScript and Python—the foundation of our fucked-up world.

Leave a Reply