What Is TypeScript?
TypeScript is an open-source language developed by Microsoft that adds a static type system on top of JavaScript. It is a strict superset of JavaScript: every valid .js file is already valid TypeScript, so you can adopt it gradually. The defining feature is the compiler, which checks your code for type errors before it ever runs and then erases the types to emit clean, standard JavaScript that runs anywhere JS does — browsers, Node.js, Deno, Bun, and edge runtimes. This page gives you the big-picture mental model; the rest of this section gets you installed and running.
A typed superset of JavaScript
Because TypeScript builds on JavaScript, anything you already know about JS still applies. TypeScript layers type annotations and type inference over the language so the compiler understands the shape of your data.
// Plain JavaScript — perfectly valid TypeScript too
function greet(name) {
return "Hello, " + name;
}
// Idiomatic TypeScript — same code, with a type annotation
function greetTyped(name: string): string {
return `Hello, ${name}`;
}
greetTyped("Ada"); // ✅ OK
greetTyped(42); // ❌ Error: Argument of type 'number' is not assignable to 'string'
The annotation : string tells the compiler what name must be. When you call greetTyped(42), the error is reported at compile time in your editor — long before the code reaches a user.
Static typing catches bugs early
The core value of TypeScript is moving entire classes of bugs from runtime to compile time. Typos, wrong argument types, missing properties, and undefined access are flagged as you type.
interface User {
id: number;
name: string;
email?: string;
}
function sendEmail(user: User) {
// ❌ Error: Property 'emial' does not exist on type 'User'.
console.log(user.emial);
// ✅ The optional property forces you to handle the missing case
if (user.email) {
console.log(user.email.toUpperCase());
}
}
Without types, user.emial would silently be undefined and surface as a vague runtime failure later. TypeScript turns it into an immediate, precise error.
Types are erased at compile time
A crucial mental model: types exist only during development and compilation. The compiler (tsc) checks them, then strips them out, producing ordinary JavaScript. There is no runtime type-checking and no performance cost from the types themselves.
// input.ts
const total: number = 19.99;
const label: string = `Price: ${total}`;
// output.js — types are gone
const total = 19.99;
const label = `Price: ${total}`;
This “type erasure” is why TypeScript runs everywhere JavaScript does, and why you sometimes need techniques like type guards to make decisions based on types at runtime — the types alone are not available once the program executes.
How TypeScript fits into your workflow
You write .ts (or .tsx for React) files, the compiler type-checks and transpiles them, and you ship the resulting JavaScript. In modern setups, tools like tsx, esbuild, Vite, or Bun handle the transpilation for fast feedback, while tsc --noEmit is used purely for type-checking in CI.
| Concept | JavaScript | TypeScript |
|---|---|---|
| File extension | .js / .jsx | .ts / .tsx |
| Type checking | None (runtime errors) | Static, at compile time |
| Tooling / autocomplete | Limited | Rich (types power IntelliSense) |
| Output | Runs directly | Compiles to .js |
| Adoption | — | Incremental, file by file |
TypeScript never changes JavaScript’s runtime behavior. If code runs in JS, it runs identically after compilation — TypeScript only adds a checking layer on top.
Why developers reach for it
Beyond catching bugs, TypeScript dramatically improves the editor experience: autocomplete, inline documentation, safe refactoring, and “go to definition” all work because the editor understands your types. On large codebases and teams, types act as always-accurate documentation and a contract between modules.
type Status = "idle" | "loading" | "success" | "error";
function render(status: Status) {
// The editor autocompletes the four valid values and rejects anything else
switch (status) {
case "idle": return "Waiting…";
case "loading": return "Loading…";
case "success": return "Done!";
case "error": return "Something went wrong.";
}
}
Literal union types like Status make invalid states unrepresentable — a small example of how TypeScript helps you model your domain precisely.
Best Practices
- Treat TypeScript as JavaScript with guardrails — you can adopt it incrementally, one file at a time.
- Enable
strictmode from day one on new projects to get the full safety benefits. - Lean on inference; only add annotations where they clarify intent or the compiler cannot infer.
- Remember types are erased — never rely on them existing at runtime.
- Use literal and union types to model your domain so invalid states cannot be expressed.
- Let your editor guide you: TypeScript’s biggest day-to-day win is tooling, not just error-catching.