This episode covers code quality beyond the compiler: unit testing with Vitest, linting with typescript-eslint, type checking with tsc --noEmit, and integrating all of it into CI. You'll also understand the role of TypeScript Server in the editor.

The compiler catches type errors, but not every logic error. That's where the next quality layer works: testing to verify behavior, linting to enforce style and prevent dangerous patterns, and an editor that understands the code for instant feedback.
These three differ from the compiler, but complement it. Tests ensure the code does the right thing. The linter ensures the code is written with agreed-upon patterns. TypeScript Server makes all of this feel alive in the editor, offering suggestions and safe refactors.
Episode 18 covers testing setup with Vitest, linting with typescript-eslint, type checking as a CI gate, and how editors leverage TypeScript Server.
Vitest is a modern test runner that understands TypeScript directly:
bun add -d vitestimport { describe, expect, it } from "vitest";
import { tambah } from "./hitung";
describe("tambah", () => {
it("menjumlahkan dua angka", () => {
expect(tambah(2, 3)).toBe(5);
});
});The tambah function is tested with a real call and an expectation. Tests give confidence that code changes don't break old behavior. Vitest reads TypeScript without extra configuration and runs fast thanks to on-the-fly transformation.
Test scripts are added to package.json:
{
"scripts": {
"test": "vitest run",
"test:watch": "vitest"
}
}vitest run executes all tests once, suitable for CI. vitest without arguments runs in watch mode, reloading tests when code changes. Both use .test.ts or .spec.ts files placed next to the code under test.
ESLint with the TypeScript plugin understands rules specific to the type system:
bun add -d eslint typescript-eslint{
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
]
}The configuration above loads ESLint's base rules plus TypeScript rules. The plugin checks things the compiler can't see, like unnecessary any usage, unhandled promises, and declarations never used. Running bun run lint becomes the second quality control after compilation.
The main command for checking types without producing files:
npx tsc --noEmitnpx tsc --noEmit runs a full check and reports errors without writing output. This is the command most often used in CI as a gate: when compilation fails, the pipeline stops. The editor runs the same check continuously through TypeScript Server.
Editors like VS Code run TypeScript Server as a background service. The server provides autocomplete, definition navigation, safe rename, and quick-fix suggestions. If TypeScript Server runs out of memory on large projects, the tsserver.maxTsServerMemory option in editor settings can be raised.
Tip
Watch the TypeScript icon in the editor's status bar to see the health of TypeScript Server. The restart button is useful whenever tsconfig or package changes don't produce the expected results.
All three tools are combined in one pipeline:
bun run lint
bun run typecheck
bun run test
bun run buildThe order above runs the linter, type checking with tsc --noEmit, tests, then build. If any step fails, the process stops and the code isn't released. This pipeline makes quality not one person's responsibility, but an automated gate that's the same for everyone.
Episode 18 completes your quality toolbox: Vitest to verify behavior, typescript-eslint to enforce patterns, tsc --noEmit as the CI gate, and TypeScript Server for real-time feedback in the editor.
Key takeaways:
tsc --noEmit checks types without producing files.In the next episode 19 we'll discuss strategies for writing scalable type definitions — designing types that stay easy to maintain as the project grows.