Chapter 1 of 24 · TypeScript for Beginners — Complete Free Tutorial

Introduction to TypeScript

Welcome to the TypeScript for Beginners series on Scriptly. This 24-chapter journey takes the JavaScript you already write and puts a static type system in front of it, so entire categories of bugs are caught while you are still typing instead of when a user reloads the page. This first chapter explains what TypeScript is, how static typing compares with the dynamic typing you know, how the tsc compiler turns .ts files into JavaScript, and where the type information goes once the program is running.

What TypeScript is

TypeScript is a programming language created by Microsoft and open-sourced in 2012. It is a strict superset of JavaScript: every valid JavaScript program is also a valid TypeScript program, and TypeScript layers extra features on top — first among them a static type system. An annotation such as : number is a promise about your data, and the compiler checks every promise before the code runs.

  • Checked at compile time — mistakes appear as red squiggles in your editor and messages in your terminal, not as exceptions in production.
  • Erased before running — the JavaScript that ships contains no annotations at all, so runtime behaviour is unchanged.
  • A superset, not a rewrite — functions, closures, promises and the DOM work exactly as they do in the JavaScript series.

Static vs dynamic typing

JavaScript is dynamically typed: variables carry values rather than type promises, so a value can quietly change category and nothing complains until the affected line actually runs. This program is broken, yet Node.js executes it without a murmur:

let total = 10;
total = total + 2;
total = "free";

console.log(total + 1);

Expected output:

free1

No error, no warning — just a nonsense result for somebody else to discover. A statically typed language insists that the types line up before anything runs, so the same mistake never gets that far:

let total: number = 10;
total = total + 2;
total = "free";

console.log(total + 1);

Expected output:

error TS2322: Type 'string' is not assignable to type 'number'.

The compiler names the offending line and stops the build. The bug is found while the fix is still a two-second edit.

QuestionJavaScript (dynamic)TypeScript (static)
When is a type mismatch found?When that line runs in a browser or on a serverWhen you compile or save in the editor
Where do types live?In values only — variables declare nothingIn annotations you write, checked by tsc
Can you pass the wrong argument?Yes, until the function misbehavesNo — the call is rejected before it runs
What runs in production?Your JavaScriptThe same JavaScript, after types are erased

How tsc compiles to JavaScript

TypeScript ships with one tool: tsc, the TypeScript compiler. Given a file it does three things in order — parses it, type-checks it against your annotations and the library definitions, then erases the types and writes out ordinary JavaScript. The browser and Node.js never see TypeScript; they only ever run the emitted .js. Put this in greeting.ts:

const greeting: string = "Hello, TypeScript!";
console.log(greeting);

Then compile and run it:

npx tsc greeting.ts
node greeting.js

Expected output:

Hello, TypeScript!

And greeting.js now contains exactly this — the annotation is gone:

const greeting = "Hello, TypeScript!";
console.log(greeting);

Expected output: identical to before, because nothing behavioural changed. One command performed the whole pipeline: parse, check, erase, emit.

Why teams pay the annotation tax

Type annotations are extra work, so why do whole companies adopt them? Because the work is repaid many times over:

  • Bugs are caught early — wrong argument order, mistyped property names, forgotten null checks and non-existent DOM elements become compile errors.
  • Refactoring becomes safe — rename a property and the compiler lists every call site you would have missed.
  • Editor tooling jumps in quality — completion, parameter hints, go-to-definition and instant inline errors all read your types.
  • Types are living documentation — a signature such as createOrder(items: CartItem[]) answers questions a comment would get wrong six months later.
  • Large teams stop guessing — the contract between two files is checked by a machine instead of argued about in review.

TypeScript on this site, and the JavaScript series

This series assumes you can already read and write JavaScript. TypeScript does not replace that knowledge — it audits it. Where a topic mirrors one you have met before, such as data types and type coercion, the difference is only that the compiler now gets a vote. If functions, closures or async still feel shaky, work through the JavaScript series first: types cannot rescue code you do not understand. Where TypeScript behaviour depends on JavaScript semantics — this, coercion, the event loop — this series links back to the chapter that explains it.

Where the type information goes

Types exist only for the compiler. When tsc emits JavaScript it deletes every annotation: no type objects, no reflection, no runtime checks, no registry of your interfaces. You can prove it with one command:

npx tsc greeting.ts
grep "string" greeting.js || echo "no type information left"

Expected output:

no type information left

This is why TypeScript adds no measurable cost to the code you ship and why the same build runs anywhere JavaScript runs. It also explains a question beginners ask constantly: you cannot ask a running program “what type is this value?” The answer at runtime is always JavaScript's answer — typeof user is "object" whether the annotation said User or Record<string, unknown>.

Any in disguise: what TypeScript does not change

The honest small print: TypeScript does not change runtime behaviour. It never validates data arriving from an API, never stops a division from producing NaN, and never adds a single check to your shipped JavaScript. Type safety is a project you maintain, not a switch you flip:

  • Incoming data is untrusted — JSON.parse(input) returns any; the compiler believes whatever shape you claim it has, and the network does not care.
  • Casts are promises, not checks — value as User tells the compiler to stop asking questions. Nothing is verified at runtime.
  • any switches the checking off — one import typed as any and you are back to dynamic typing with a .ts extension.
  • Runtime bugs still happen — bad JSON, mutable shared state, timezone surprises and stale variables are exactly as possible as before.

Think of tsc as a tireless reviewer who has read every line and knows every contract, but who never executes anything. Trust it inside your code; distrust every boundary that leaves it.

Common mistakes

  • Believing TypeScript removes all bugs — the compiler only proves what you told it; data from APIs and users still has to be validated when it arrives.
  • Throwing away JavaScript knowledge — TypeScript is a superset; keep your existing idioms and add annotations gradually rather than learning a “new language”.
  • Expecting types at runtime — annotations are erased, so asking a running program for “the type of” a value returns JavaScript's typeof answer, nothing more.
  • Reaching for any at the first red squiggle — it silences the compiler and hides the very bugs you installed TypeScript to catch; prefer unknown until you know.
  • Skipping the JavaScript fundamentals — types do not replace understanding closures, async/await or array methods; revisit them whenever an example here reads backwards.

Practice checklist

  1. Type the dynamic total example into a .js file, run it with Node, and confirm the output really is free1.
  2. Compile the typed version with npx tsc and read the TS2322 message until every word of it makes sense.
  3. Pick a function from the JavaScript series, annotate its parameters, and compile it.
  4. Run npx tsc greeting.ts, open greeting.js, and confirm not a single annotation survived.
  5. Write one paragraph in your own words answering: what does TypeScript check, when does it check it, and what does it never check?

What's next?

You know what the compiler does and where the types go; now it is time to install it and run your first real build — continue with Setting Up TypeScript.

Quiz: Introduction to TypeScript

5 questions · 70% to pass

Frequently Asked Questions

TypeScript is a strict superset of JavaScript that adds a static type system. You annotate the promises your data makes, the compiler checks every one before the code runs, then erases the types and emits ordinary JavaScript. You catch wrong arguments, typoed properties and forgotten null checks while you are still typing, and refactoring plus editor autocomplete both improve sharply.

You should. This series assumes you can already read and write JavaScript: functions, arrays and objects, closures, modules and async/await. TypeScript audits that knowledge rather than replacing it, and types cannot rescue code you do not understand. If those fundamentals feel shaky, work through the JavaScript for Beginners series on this site first, and this course will go much faster.

No. TypeScript is compiled, not interpreted: tsc parses your file, type-checks it, then erases every annotation and writes ordinary JavaScript. The browser and Node.js only ever run that emitted .js, so runtime behaviour, performance and debugging are unchanged. It also never validates API data at runtime, so the series teaches you to distrust every boundary that leaves the compiler.

If you already know JavaScript, TypeScript is a modest step rather than a new language: the syntax is the same plus a colon. Most beginners read their first red squiggle within days and feel comfortable in two to four weeks of regular practice. The curve steepens later around generics and advanced types, which is where this 24-chapter series deliberately spends its second half.

Both name the same object shapes, so either works for a simple contract. The differences decide the rest: only interfaces merge when declared twice, while only type aliases can express unions, primitives, tuples and mapped types. The series default is interface for props and exported shapes other files consume, and type for everything else. Pick one style per project and stay consistent.

strict: true is a bundle of safety switches, not a single behaviour. It turns on noImplicitAny, so every parameter must be inferrable, strictNullChecks, so null and undefined are their own types you must narrow, strictFunctionTypes, noImplicitThis, strictPropertyInitialization, and useUnknownInCatchVariables, which types catch values as unknown. Every example in this series compiles with it on.

any accepts anything and switches the compiler off, so one any spreads through inference into everything derived from it and you are back to dynamic typing with a .ts extension. unknown accepts anything too, but forbids property access until you narrow it with a typeof or instanceof check. The series pushes you toward unknown because it keeps the check you installed TypeScript to get.

Yes to both. Vite understands TypeScript natively: scaffold the vanilla-ts or react-ts template and a tsconfig.json is waiting for you, with moduleResolution set for the bundler. JSX belongs in .tsx files, props get an interface, and hooks infer or accept explicit type arguments. The series covers typed React in depth, from props, state and events to refs, context and fetch effects.

Enums still compile and work fine in TypeScript 5.x, but they are no longer the default recommendation. A union of string literals such as "light" | "dark" needs no runtime object, matches JSON directly and plays well with single-file transpilers like Vite's esbuild. Keep enum for existing codebases that already use it, avoid const enum, and prefer literal unions in new code.

Adopt it incrementally instead of rewriting. Install typescript, add a tsconfig.json with allowJs and noEmit so .js and .ts coexist while your bundler keeps building, then check individual files with a // @ts-check comment. Rename leaf modules to .ts one at a time, fix implicit any errors first, and gate every pull request with tsc --noEmit so untyped code cannot sneak back in.