---
title: "01 — Why TypeScript: Static Types on Top of JavaScript"
uid: why-typescript
tags: ["compiler", "tooling", "typescript", "roadmap:typescript", "fundamentals"]
excerpt: "TypeScript is JavaScript plus a compile-time type checker — the types are erased before the code runs. A development-time tool that vanishes at runtime."
date: 2026-08-13T03:27:30+0000
source: https://www.aveshina.my.id/en/blog/why-typescript
---

"Just JavaScript with extra syntax" was my TypeScript summary, and it put the emphasis on the wrong half. The idea that everything else hangs off: **TypeScript is a superset of JavaScript that adds a compile-time type checker, and the types are erased before the code ever runs.** [1] The runtime is plain JavaScript; the types are a development-time tool that vanishes.

The framing that finally landed is the timeline. JavaScript is dynamically typed — the engine figures out types as it runs, which means a typo in a property name or a string where I expected a number only surfaces when that line executes, in front of a user. TypeScript moves that check to *before* the code runs. I write .ts files, a compiler reads them, flags anything that doesn't match the declared shapes, and then emits plain .js with every type annotation stripped out. The browser never sees TypeScript [1][2].

```figure
<svg viewBox="0 0 740 280" xmlns="http://www.w3.org/2000/svg" class="my-6 w-full max-w-2xl" role="img" aria-label="The TypeScript compile pipeline. Left: a .ts file with type annotations. Middle: the tsc compiler checks the types against declared shapes and flags errors before running. Right: plain .js output with all annotations removed, ready for the browser.">
  <defs>
    <marker id="tsarrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto">
      <path d="M0,0 L10,5 L0,10 z" fill="#64748b"/>
    </marker>
  </defs>
  <g font-family="ui-sans-serif, system-ui, sans-serif" text-rendering="geometricPrecision">

    <!-- .ts source -->
    <rect x="30" y="50" width="180" height="170" rx="8" fill="#e0e7ff" stroke="#6366f1" stroke-width="1.5"/>
    <text x="120" y="72" font-size="13" font-weight="700" fill="#1e1b4b" text-anchor="middle">user.ts</text>
    <rect x="46" y="86" width="148" height="22" rx="4" fill="#c7d2fe" stroke="#6366f1" stroke-width="1"/>
    <text x="120" y="101" font-size="10.5" font-family="ui-monospace, monospace" fill="#1e1b4b" text-anchor="middle">name: string</text>
    <rect x="46" y="114" width="148" height="22" rx="4" fill="#c7d2fe" stroke="#6366f1" stroke-width="1"/>
    <text x="120" y="129" font-size="10.5" font-family="ui-monospace, monospace" fill="#1e1b4b" text-anchor="middle">age: number</text>
    <text x="120" y="165" font-size="10" font-style="italic" fill="#475569" text-anchor="middle">types present</text>

    <!-- compiler -->
    <rect x="280" y="90" width="160" height="90" rx="8" fill="#fef9c3" stroke="#ca8a04" stroke-width="1.5"/>
    <text x="360" y="120" font-size="14" font-weight="700" fill="#422006" text-anchor="middle">tsc</text>
    <text x="360" y="140" font-size="10.5" fill="#475569" text-anchor="middle">type-check</text>
    <text x="360" y="156" font-size="10.5" fill="#475569" text-anchor="middle">then erase types</text>

    <!-- .js output -->
    <rect x="530" y="50" width="180" height="170" rx="8" fill="#dcfce7" stroke="#16a34a" stroke-width="1.5"/>
    <text x="620" y="72" font-size="13" font-weight="700" fill="#052e16" text-anchor="middle">user.js</text>
    <rect x="546" y="86" width="148" height="22" rx="4" fill="#bbf7d0" stroke="#16a34a" stroke-width="1"/>
    <text x="620" y="101" font-size="10.5" font-family="ui-monospace, monospace" fill="#052e16" text-anchor="middle">name</text>
    <rect x="546" y="114" width="148" height="22" rx="4" fill="#bbf7d0" stroke="#16a34a" stroke-width="1"/>
    <text x="620" y="129" font-size="10.5" font-family="ui-monospace, monospace" fill="#052e16" text-anchor="middle">age</text>
    <text x="620" y="165" font-size="10" font-style="italic" fill="#475569" text-anchor="middle">types gone — runs in browser</text>

    <!-- arrows -->
    <path d="M210,135 L278,135" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#tsarrow)"/>
    <path d="M440,135 L528,135" fill="none" stroke="#64748b" stroke-width="1.5" marker-end="url(#tsarrow)"/>

    <!-- bottom note -->
    <text x="370" y="255" font-size="11" fill="#64748b" text-anchor="middle" font-style="italic">types are a development-time tool; the runtime is plain JavaScript</text>
  </g>
</svg>
```

## Superset, not a different language

The first thing to internalize: every valid .js file is already a valid .ts file [1]. TypeScript doesn't replace JavaScript syntax — it extends it. I can rename utils.js to utils.ts and the compiler will accept it. This is what makes adoption incremental rather than a rewrite, and it's the reason the language spread the way it did: existing JavaScript isn't a liability, it's a starting point.

The relationship also runs the other way. TypeScript is designed to interoperate with JavaScript seamlessly [2]. I can import a JavaScript library into a TypeScript project, and gradually migrate files one at a time. The compiler can even type-check plain .js files with the right options turned on. The boundary between the two is soft, which is the whole point.

## What the types actually buy

The payoff is catching a category of bug at the desk instead of in production. The classic shape of that bug:

```
function greet(user: { name: string }) {
  return "Hello, " + user.nmae.toUpperCase();
  //                       ^^^^ Property 'nmae' does not exist.
}
```

In plain JavaScript, user.nmae is undefined, .toUpperCase() throws, and the user sees a blank screen. TypeScript flags the typo with a red underline before I ever run the code [1][2]. Multiply that across a codebase — wrong argument types, missing fields on an API response, a refactor that forgot one call site — and the savings compound. The types also double as always-current documentation: a function signature tells me exactly what it accepts and returns without reading the implementation.

## The compiler: tsc

The engine doing the checking is tsc, the TypeScript compiler [3]. It reads .ts files, validates them against the type system, and emits .js. The emit step matters — TypeScript isn't just a linter, it's a transpiler. I tell it which JavaScript version to target (ES5 for old browsers, ESNext for modern), and tsc down-converts syntax as needed. Running tsc from the CLI with no arguments compiles a project according to its config; flags like --noEmit make it type-check only without writing files [3].

For day-to-day work I rarely invoke tsc directly. Bundlers and frameworks wrap it. But knowing it's there matters when a type error appears in a build log — tsc is the thing producing that error.

## ts-node and the playground

Two tools smooth out the iteration loop. **ts-node** runs .ts files directly without a separate compile step — it executes TypeScript in a Node.js environment by compiling on the fly, which is invaluable for scripts and quick experiments [4]. The **TypeScript Playground** is the browser equivalent: an official online editor where I can type TypeScript, see compilation errors and the emitted JavaScript in real time, and share links to snippets [5]. When I'm unsure how a type feature behaves, the playground answers faster than a full project setup.

## Installation and the config file

Getting started is one install and one config file. TypeScript ships as an npm package, and a project's settings live in tsconfig.json — a declarative file at the root that tells tsc how to behave: which files to include, what JavaScript to target, how strict to be [6][7]. That config is what makes a TypeScript build reproducible; it's checked into the repo and shared by every developer and CI run. I'll dig into the specific options in the next notes — the takeaway here is just that the config *exists* and is the single source of truth for how the compiler treats the project.

## How I use this

The way of thinking I keep is the timeline: types live at design and compile time, JavaScript lives at runtime, and the two never meet in the shipped code. When a type error confuses me, I ask whether I'm reasoning about the type system (what tsc sees) or the runtime (what the browser sees) — most of my confusion came from blurring them. And when I adopt TypeScript in an existing JavaScript project, I lean on the superset property: rename files, leave noImplicitAny off at first, and tighten the config over weeks rather than all at once.

## References

[1] Microsoft, "TypeScript Handbook: TypeScript in 5 minutes," typescriptlang.org, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html](https://www.typescriptlang.org/docs/handbook/typescript-from-scratch.html)

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

[3] Microsoft, "tsc CLI Options," typescriptlang.org, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/compiler-options.html#using-the-cli](https://www.typescriptlang.org/docs/handbook/compiler-options.html#using-the-cli)

[4] TypeStrong, "ts-node — TypeScript execution and REPL for Node.js," GitHub, 2024. [Online]. Available: [https://github.com/TypeStrong/ts-node](https://github.com/TypeStrong/ts-node)

[5] Microsoft, "TypeScript Playground," typescriptlang.org, 2024. [Online]. Available: [https://www.typescriptlang.org/play](https://www.typescriptlang.org/play)

[6] Microsoft, "Install and Configure TypeScript," typescriptlang.org, 2024. [Online]. Available: [https://www.typescriptlang.org/download](https://www.typescriptlang.org/download)

[7] Microsoft, "What is a tsconfig.json," typescriptlang.org, 2024. [Online]. Available: [https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#handbook-content](https://www.typescriptlang.org/docs/handbook/tsconfig-json.html#handbook-content)

```quiz
Q: TypeScript is best described as…
- a completely separate language that compiles to JavaScript
- a superset of JavaScript that adds a compile-time type checker, with types erased at emit
correct: 1
explain: Every valid JavaScript file is valid TypeScript. The language adds static types that the compiler checks and then strips out — the runtime is plain JavaScript.

Q: When does TypeScript catch a type error like a misspelled property name?
- At runtime, when the line executes
- At compile time, before the code ever runs
correct: 1
explain: TypeScript moves type checks to before the code runs. The compiler flags the typo, and the types themselves are erased so the runtime JavaScript never sees them.

Q: What does `tsc` do?
- Only lints code style
- Type-checks TypeScript and emits (possibly down-converted) JavaScript
correct: 1
explain: `tsc` is both a type checker and a transpiler — it validates types and emits JavaScript for the target runtime.

Q: You want to run a `.ts` script directly without a separate compile step. Reach for…
- `tsc --run`
- `ts-node`
correct: 1
explain: `ts-node` executes TypeScript in a Node.js environment by compiling on the fly, so `.ts` files run directly.

Q: A project's compiler settings (target, strictness, included files) live in…
- package.json
- tsconfig.json
correct: 1
explain: `tsconfig.json` is the declarative config file at the project root that tells `tsc` how to behave, and it's checked in so the build is reproducible.
```
