A lesson that cost me real debugging time: I ran a heavy tsc --noEmit over a project whose generated Prisma client is enormous, saw exit code 0 and zero error lines, and reported the typecheck clean. It wasn’t. tsc had run out of memory and crashed — node printed a V8 stack trace to stderr and died, but the wrapper returned 0, and my grep for error lines matched nothing. Real type errors sailed through undetected.
The tells of a silent OOM: frames like Builtins_InterpreterEntryTrampoline or node::StartExecution in the unfiltered tail of the output.
The fixes: cap memory explicitly (NODE_OPTIONS="--max-old-space-size=4096") so it fails loudly instead of thrashing; scope the check to a small tsconfig covering only the files you changed instead of the whole monorepo; and treat “exit 0 with suspiciously empty output” from a heavy tsc run as a failure until you’ve seen the raw tail.