Emmanuel Akinfulubi

TypeScript, for people who already write JavaScript.

Eighteen lessons on the type layer and nothing else. No JavaScript revision, no framework, no build tooling you did not ask for — just the part that is genuinely new, taught against the real compiler.

What you are getting

You know how to write JavaScript. That means roughly two thirds of the usual TypeScript tutorial is wasted on you, and the third that matters — describing the shape of data before it exists — tends to get four paragraphs. This course inverts that.

The whole course in one panel

// A shape, described once.
interface Order {
  id: string;
  customer: { name: string; email?: string };
  lines: readonly { sku: string; quantity: number }[];
  placedAt: Date;
}

// A function whose contract is written down rather than remembered.
function summarise(order: Order): string {
  const units = order.lines.reduce((total, line) => total + line.quantity, 0);
  const contact = order.customer.email ?? 'no email on file';
  const day = order.placedAt.toISOString().slice(0, 10);
  return `${order.id}: ${units} units for ${order.customer.name} (${contact}) on ${day}`;
}

const order: Order = {
  id: 'A-1001',
  customer: { name: 'Ada' },
  lines: [
    { sku: 'desk', quantity: 1 },
    { sku: 'chair', quantity: 4 },
  ],
  placedAt: new Date('2026-01-15T09:30:00Z'),
};

console.log(summarise(order));

// Every one of these is now a compile error rather than a 3am incident:
//
//   order.custmer.name           misspelled property        TS2551
//   order.placedAt.slice(0, 10)  Date has no slice          TS2339
//   order.customer.email.trim()  might not be there         TS18048
//   order.lines.push({ ... })    lines is readonly          TS2339
//   summarise({ id: 'A-2' })     missing properties         TS2345
//
// Open "What this compiles to" below. Not one of those checks costs you a
// single line at run time, because none of the types are there any more.
What this compiles to
// A function whose contract is written down rather than remembered.
function summarise(order) {
    const units = order.lines.reduce((total, line) => total + line.quantity, 0);
    const contact = order.customer.email ?? 'no email on file';
    const day = order.placedAt.toISOString().slice(0, 10);
    return `${order.id}: ${units} units for ${order.customer.name} (${contact}) on ${day}`;
}
const order = {
    id: 'A-1001',
    customer: { name: 'Ada' },
    lines: [
        { sku: 'desk', quantity: 1 },
        { sku: 'chair', quantity: 4 },
    ],
    placedAt: new Date('2026-01-15T09:30:00Z'),
};
console.log(summarise(order));

// Every one of these is now a compile error rather than a 3am incident:
//
//   order.custmer.name           misspelled property        TS2551
//   order.placedAt.slice(0, 10)  Date has no slice          TS2339
//   order.customer.email.trim()  might not be there         TS18048
//   order.lines.push({ ... })    lines is readonly          TS2339
//   summarise({ id: 'A-2' })     missing properties         TS2345
//
// Open "What this compiles to" below. Not one of those checks costs you a
// single line at run time, because none of the types are there any more.

Output


      

Part one — The type layer

What TypeScript is, and the four ideas that cover most day-to-day code. Read these in order.

  1. 01 What TypeScript Actually Is Explain what survives compilation and what vanishes, and why that matters. 14 min
  2. 02 Inference, and When to Annotate Let inference do the work, and annotate only where it pays. 15 min
  3. 03 Objects, Interfaces and Structural Typing Describe any object shape, and explain why a name never has to match. 18 min
  4. 04 Unions and Literal Types Model a value that can be one of several things, precisely. 17 min
  5. 05 Narrowing Take a union apart safely, and know which checks the compiler trusts. 18 min
  6. 06 Functions Type any function, including callbacks and optional parameters. 17 min

Part two — Making it hold

Absence, generics and classes. This is where the checking starts paying for itself.

  1. 07 Arrays, Tuples and readonly Type arrays and tuples, and use readonly where it helps. 15 min
  2. 08 The Absence of a Value Handle missing values deliberately instead of hoping. 18 min
  3. 09 Generics Write a function or class that works over any type, and keep the types connected. 20 min
  4. 10 Constraints, keyof and Lookups Constrain a generic, and derive one type from another. 18 min
  5. 11 Classes Type a class hierarchy, and choose between private and #. 19 min
  6. 12 any, unknown and never Reach for unknown instead of any, and read never correctly. 16 min

Part three — Real projects

Declarations, derived types, configuration, and moving an existing codebase across.

  1. 13 Type Predicates and Assertions Write a type guard, and know when you are lying to the compiler. 17 min
  2. 14 Enums, and What to Use Instead Model a fixed set of values without reaching for enum. 15 min
  3. 15 Modules and Declaration Files Split types across files and describe an untyped library. 17 min
  4. 16 The Utility Types Derive the type you need from one you already have. 18 min
  5. 17 Mapped and Conditional Types Read a mapped or conditional type without flinching, and write a simple one. 21 min
  6. 18 tsconfig, and Moving JavaScript Across Configure a project deliberately and migrate JavaScript incrementally. 19 min

Nothing marked done yet. Lesson 01 is a good place to start.

How this course is different from the last two

The OOP and algorithms courses could run every sample in your browser directly, because browsers run JavaScript. Browsers do not run TypeScript, and shipping a compiler to this page would mean several megabytes of download for a course that promises to work offline.

So the arrangement here is different, and better. Every sample is compiled by the real TypeScript compiler when this site is built. What you press Run on is the JavaScript that compiler produced from the exact source you are reading. And because the compiler is involved at build time, the course can do something the other two could not:

Errors are real

A panel marked with an error code in its corner does not compile, and the message underneath it is the compiler's own words, captured during the build. If the compiler ever changed its mind, this site would fail to build until the page was corrected.

81 such panels, across 36 distinct error codes

Claims are checked

Every lesson declares which error code each counter-example should produce. The build compares that against reality and stops if they disagree. That mechanism caught fifteen mistakes in my own writing, several of which were confidently wrong.

See the README for the list

Erasure is visible

Twenty panels carry a “what this compiles to” disclosure containing the real emitted JavaScript. Nothing explains that types vanish as convincingly as watching them be absent from the output.

Try the panel above

Editing has a caveat

The one honest loss. There is no compiler in your browser, so an edited panel runs as plain JavaScript — annotations become syntax errors. The playground teaches the local workflow instead, which is what you would actually use.

playground.html

How to work through it

The order matters in one place. Lessons 03 to 05 are a single argument — shapes, then unions, then how the compiler reads your if statements — and unions feel arbitrary until narrowing explains them. Read those three together.

  1. Read with this page in one window and a scratch.ts file in the other. Every lesson ends with exercises meant for that file.
  2. Run the panels. When a counter-example surprises you, read the compiler's message rather than my prose about it — the message is the primary source.
  3. Keep npx tsc --noEmit scratch.ts in a terminal. Your editor is doing the same thing continuously, but running it yourself once makes the relationship obvious.
  4. Skip lesson 17 on a first pass if you want to. It is the deep end and it says so.

What this does not cover

React, Vue, Node APIs, bundler configuration, decorators, and the details of publishing a typed package. Those are all worth learning and none of them are TypeScript — they are things you use TypeScript with. Lesson 18 covers enough configuration to set up a real project and stops there.

There is also no coverage of JavaScript itself. If this, prototypes, closures or async are shaky, the OOP course is the better starting point, because TypeScript will not make an unfamiliar language clearer.

Everything else

What this is, honestly

A course written to be read offline. No account, no backend, no analytics, no network request of any kind at read time: the fonts are inlined into the stylesheet and the syntax highlighter is a few hundred lines in assets/js/highlight.js. Your progress and anything you type into a panel live in this browser's localStorage under the ts. prefix and nowhere else.

The one dependency is at build time. Producing this site requires the TypeScript compiler, because checking the samples honestly is the entire point. Reading it requires nothing.

Newsletter

New writing, when it lands.

Occasional notes on shipping software with AI agents. No schedule, no filler, unsubscribe in one click.