Why TypeScript?
The bug TypeScript is designed to catch
Section titled “The bug TypeScript is designed to catch”Here is plain JavaScript that looks fine and fails at runtime:
function greet(user) { return "Hello, " + user.name.toUpperCase();}
greet({ nam: "Ada" }); // typo: `nam` not `name`// 💥 TypeError: Cannot read properties of undefined (reading 'toUpperCase')The typo isn’t found until that line executes — maybe in production, maybe for one unlucky user. TypeScript moves that discovery to compile time:
function greet(user: { name: string }) { return "Hello, " + user.name.toUpperCase();}
greet({ nam: "Ada" });// ❌ Error: Object literal may only specify known properties,// and 'nam' does not exist in type '{ name: string }'.You find out the instant you write it, in the editor, before anything runs. That shift — from “fails later, somewhere” to “fails now, right here” — is the whole value proposition.
Compile time vs runtime: the crucial split
Section titled “Compile time vs runtime: the crucial split”TypeScript has two completely separate phases, and confusing them is the source of most beginner mistakes.
flowchart LR ts[".ts source (types + logic)"] -->|tsc: type-check| check["Type checking (errors reported here)"] ts -->|tsc: emit| js[".js output (types ERASED)"] js --> run["Runtime (no types exist)"]
- Compile time — the type checker reads your annotations and inferred types and reports errors. This is where all the safety happens.
- Runtime — the emitted JavaScript runs. The types are gone. They were erased during compilation.
This is why you cannot do this:
function isString(x: unknown) { return x is string; // ❌ not a thing — types don't exist at runtime}At runtime there is no string type to check against — only the JavaScript value. To narrow types at runtime you use real JavaScript operations (typeof x === "string") that the compiler understands. More on that in the narrowing lesson.
Type erasure, concretely
Section titled “Type erasure, concretely”Compile this:
interface User { name: string; age: number; }const u: User = { name: "Ada", age: 36 };function older(u: User): number { return u.age + 1; }The emitted JavaScript is:
const u = { name: "Ada", age: 36 };function older(u) { return u.age + 1; }The interface vanished entirely; the annotations are stripped. Types add zero runtime cost and zero runtime behavior — they are a conversation between you and the compiler that ends before the program runs.
The honest costs
Section titled “The honest costs”TypeScript is not free, and pretending otherwise does you a disservice:
- A build step. You need
tscor a bundler that strips types. Plain.tsdoesn’t run in Node or the browser directly (though runtimes are increasingly adding type-stripping). - A learning curve. The type system is a language of its own — this whole course exists because of that.
- Type/runtime gap. The compiler can be wrong about runtime reality — an
any, a bad cast, or unvalidated external data can lie to it. Types are a model, not a guarantee about the outside world (the Practical Mastery module is largely about this).
The trade is almost always worth it for anything beyond a throwaway script: you pay a fixed tax and get a large, compounding return in caught bugs, safe refactoring, and editor intelligence.