---
title: "15 — The Ecosystem: Formatting, Linting, and Build Tooling"
uid: typescript-ecosystem
tags: ["build-tools", "eslint", "typescript", "roadmap:typescript", "ecosystem", "zod", "prettier"]
excerpt: "Three tool categories, one job each — formatting (how code looks), linting (whether it follows rules), building (how it compiles and bundles) — plus type-adjacent libraries around the compiler."
date: 2026-08-13T03:27:26+0000
source: https://www.aveshina.my.id/en/blog/typescript-ecosystem
---

"A bunch of config files to copy from the last project" was my ecosystem model, and it never explained what any file did. Writing it down separated the concerns: **three tool categories each own one job — formatting (how code looks), linting (whether code follows rules), and building (how code compiles and bundles) — and around them sits a small set of type-adjacent libraries that extend what the compiler can do.** [1] Once each tool's job was distinct in my head, configuring a project stopped being copy-paste.

The framing is that the type checker (tsc) is only one layer. It catches type errors. But a real project also needs its code to look consistent across a team, to follow agreed-upon rules beyond just types, and to compile and bundle efficiently for production. Each of those is a separate tool, and conflating them is where most of my tooling confusion came from.

## Formatting: how code looks

A **formatter** automatically adjusts code style — spacing, indentation, line breaks, quotes — so that everyone's code looks the same [2]. **Prettier** is the dominant choice: it's opinionated, it parses the code and rewrites it in one canonical style, and it integrates with editors to format on save. The point isn't that Prettier's style is objectively best; it's that *one* style eliminates every "tabs vs. spaces" argument on a team. Once formatting is automated, code review stops surfacing nitpicks and focuses on substance.

A newer contender, **Biome** (formerly Rome), combines formatting and linting in one fast Rust-based tool [3]. It's an effort to replace the Prettier-plus-ESLint pair with a single, faster tool. For greenfield projects it's increasingly viable; for existing projects the Prettier + ESLint combo remains the established default.

## Linting: whether code follows rules

A **linter** analyzes code for potential errors, suspicious patterns, and style/rules that a formatter doesn't catch [4]. **ESLint** is the standard: it's configurable with rules that enforce everything from "no unused variables" to "no implicit any" to project-specific conventions. Where Prettier handles *how* the code looks, ESLint handles *what* the code does — catching bugs and enforcing practices a formatter can't see.

The split worth internalizing: **formatters and linters cover different ground, and running both is normal.** A formatter never flags a logic smell; a linter never reformats your indentation (or shouldn't — ESLint's stylistic rules are increasingly handed off to formatters). The modern setup is Prettier for formatting plus ESLint (with @typescript-eslint) for TypeScript-aware rules, each owning its lane.

## Build tools: compilation and bundling

**Build tools** handle turning source into something that runs — compiling TypeScript, bundling modules, and preparing assets for production [5]. The options the roadmap highlights sit on a spectrum of speed and configuration:

- **webpack** — the long-standing bundler. Powerful and mature, with a large plugin ecosystem, but configuration-heavy and slower for large projects.
- **Vite** — the modern default for many new projects. Uses native ES modules in development for instant startup, and Rollup for production builds. Fast dev server is its headline feature.
- **esbuild** — an extremely fast Go-based bundler/minifier, often used as a lower-level tool inside others [6].
- **swc** — a super-fast Rust-based compiler, used by frameworks (Next.js, etc.) to compile TypeScript/JSX at speed [7].
- **Parcel** — a zero-configuration build tool that aims to work out of the box with minimal setup.

The practical pattern: for new frontend projects, Vite is the common starting point; for libraries or Node projects, tsup (built on esbuild) or a direct tsc emit; for frameworks like Next.js, the build tool is chosen and configured by the framework. The unifying trend is speed — esbuild and swc, both written in systems languages, have replaced tsc itself for the *emit* step in many setups, while tsc is reserved for type-checking.

## Type-adjacent libraries: extending the compiler

Beyond the build chain, a few libraries extend what TypeScript can express or check. The roadmap points at several, and two categories matter most to me:

**Runtime validation that mirrors types.** **Zod** is the standout — a schema-first validation library where I define a schema in Zod and derive a TypeScript type from it [8]. This closes the gap between compile-time types and runtime data: the same schema validates incoming JSON *and* provides the type for the rest of the code. For API boundaries, form inputs, and any data crossing a trust boundary, Zod means my runtime checks and my types can't drift apart.

**Type-aware developer tools.** ts-node (running TypeScript directly), ts-morph (programmatic AST manipulation for codemods), ts-jest (TypeScript support in Jest), and typesync (auto-installing @types/* packages) are utilities that make working in a TypeScript codebase smoother. Each addresses a specific workflow pain; none are mandatory, but they're worth knowing when the need arises.

## How I use this

My defaults: **Prettier** (or Biome on greenfield) for formatting, format-on-save in the editor, no formatting debates in review; **ESLint** with @typescript-eslint for rules, keeping stylistic rules minimal and letting the formatter own appearance; and the project-appropriate **build tool** — Vite for new frontend work, tsup or tsc for libraries, whatever the framework picks for app frameworks. For data crossing trust boundaries (API responses, form input), I reach for **Zod** to validate at runtime and derive the type from the same schema, so the two can't drift. The discipline that pays off is keeping each tool in its lane — formatter for looks, linter for rules, compiler for types, build tool for output — and resisting the urge to make one tool do another's job, which is where configurations become tangled.

## References

[1] The New Stack, "What Is TypeScript?," 2024. [Online]. Available: [https://thenewstack.io/what-is-typescript/](https://thenewstack.io/what-is-typescript/)

[2] Prettier, "What is Prettier?," 2024. [Online]. Available: [https://prettier.io/docs/en/why-prettier.html](https://prettier.io/docs/en/why-prettier.html)

[3] Biome, "Biome — Format and lint your code," 2024. [Online]. Available: [https://biomejs.dev](https://biomejs.dev)

[4] ESLint, "Introduction to ESLint," 2024. [Online]. Available: [https://eslint.org/](https://eslint.org/)

[5] Vite, "Vite — Next Generation Frontend Tooling," 2024. [Online]. Available: [https://vitejs.dev](https://vitejs.dev)

[6] evanw, "esbuild — An extremely fast bundler and minifier," 2024. [Online]. Available: [https://esbuild.github.io/](https://esbuild.github.io/)

[7] swc, "swc — a super-fast TypeScript/JavaScript compiler written in Rust," 2024. [Online]. Available: [https://swc.rs/](https://swc.rs/)

[8] Zod, "Zod — TypeScript-first schema validation," 2024. [Online]. Available: [https://zod.dev/](https://zod.dev/)

```quiz
Q: A formatter like Prettier is responsible for…
- catching logic bugs and rule violations
- automatically adjusting code style — spacing, indentation, line breaks — so code looks consistent
correct: 1
explain: Formatters own appearance. They rewrite code into one canonical style, eliminating style debates. They do not catch logic smells; that's the linter's job.

Q: What does ESLint do that Prettier does not?
- reformat indentation
- analyze code for potential errors, suspicious patterns, and enforce rules beyond style
correct: 1
explain: ESLint handles what the code does — unused variables, implicit any, project conventions. Prettier handles how the code looks. Running both, each in its lane, is standard.

Q: Why have esbuild and swc largely replaced `tsc` for the emit step in many projects?
- they produce smaller output
- they're written in systems languages (Go/Rust) and are dramatically faster at compiling/bundling
correct: 1
explain: esbuild and swc compile and bundle far faster than tsc. Many setups use them for emit/bundling and reserve tsc for type-checking only.

Q: Zod's key value in a TypeScript project is…
- it formats code
- it lets you define a schema that validates at runtime AND provides the compile-time type, so the two can't drift
correct: 1
explain: With Zod, the runtime validation schema and the TypeScript type derive from one source. This closes the gap between compile-time types and runtime data at trust boundaries.

Q: For a new frontend project, a common modern build-tool starting point is…
- webpack with manual config
- Vite, using native ES modules in dev and Rollup for production
correct: 1
explain: Vite gives instant dev startup via native ES modules and fast production builds via Rollup. webpack remains common in existing projects, but Vite is the frequent greenfield choice.
```
