Skip to content

JavaScript and TypeScript — a learning library

JavaScript and TypeScript, one question per page. What == does before it compares, what a closure keeps, why a resolved promise beats a zero-millisecond timer, what "😀".length counts, and, on the TypeScript side, what a type promises, what tsc checks, and where the types stop telling the truth. Every claim on a finished page is backed by a program whose output is checked against a recorded answer key in CI, on Ubuntu and on macOS.

TypeScript is JavaScript with types that are checked and then erased, so the two share one library: the JavaScript chapters come first, and the TypeScript chapters build on them.

225 pages in 33 chapters: 11 checked, 3 reference, 211 stubs; 7 katas. A stub is an outline — the claim, the questions the finished page will answer and the examples it will need — and says so at the top. It becomes a lesson when its first example runs in CI.

Start here

00 — Start here — who this is for, how a page is laid out, and how a page is checked: one function run by Node and checked by tsc. To look up a single term, use the keyword index; every exercise, with its checked solution, is listed in Katas.

The chapters

JavaScript, the language

Values, equality, scope, functions, objects, collections, text, numbers, control flow, errors, async, modules, regular expressions, dates, metaprogramming and memory.

Chapter What it covers Pages
01 Running JavaScript What a line of JavaScript does depends on how it was run — script or module, strict or sloppy, and on which Node — so the first skill is knowing which of those you are in. 7 (all stubs)
02 Values and types JavaScript has eight types — seven immutable primitives and the object — and most surprises about values come from the line between them: typeof null, wrapper objects, and assignment that copies a primitive but shares an object. 10 (10 checked)
03 Equality and coercion Most JavaScript operators convert their operands before they work, and every famous surprise — [] == false, "1" + 1, null >= 0 — follows from a handful of fixed conversion rules you can run and read. 7 (all stubs)
04 Variables and scope A JavaScript variable is a binding in a scope, and nearly every scope surprise comes down to when a binding is created, how far it reaches, and whether a loop turn or a function call gets its own. 7 (all stubs)
05 Functions A function is an object like any other, stored, passed and given properties, and for every function except an arrow, this is chosen by the call rather than by the place the function was written. 8 (all stubs)
06 Objects An object is a set of properties keyed only by strings and symbols, and the everyday tools that copy or lock one — spread, Object.assign, Object.freeze — stop at the first level, leaving nested objects shared and writable. 8 (all stubs)
07 Prototypes and classes Every object inherits through a chain of prototypes, and a class is a constructor function that builds that chain for you, with a few rules of its own: new is required, the body is strict, and this waits for super(). 8 (all stubs)
08 Arrays and collections An array is an object with string index keys and a self-adjusting length, and its defaults (string sorting, skipped holes, methods that change it in place) catch everyone once; Map, Set, WeakMap and typed arrays exist for the jobs an array or a plain object does badly. 9 (all stubs)
09 Strings and Unicode A JavaScript string is a list of UTF-16 code units, so length, iteration, comparison, case and bytes each have a code-unit answer that differs from what a reader would call a character. 9 (all stubs)
10 Numbers and math Every JavaScript number is a 64-bit binary double, so exactness ends at 2^53 and at the first decimal fraction, and each page in this chapter is a consequence of that or a way around it. 8 (all stubs)
11 Control flow and iteration Every way of walking a sequence in JavaScript — for...of, spread, destructuring, generators, iterator helpers and for await — runs on one small protocol, [Symbol.iterator]() and next(), while the older statements keep rules of their own: switch compares with === and && returns an operand. 8 (all stubs)
12 Errors JavaScript lets you throw any value and catch it anywhere up the stack, but only an Error carries a stack and a cause, and an error nobody catches — thrown or rejected — ends a Node process with exit status 1. 6 (all stubs)
13 Async and the event loop JavaScript runs one piece of code at a time on one thread, and callbacks, promises, await and timers are all ways of queueing the next piece — so what runs when is decided by queues, not by threads. 11 (all stubs)
14 Modules Node runs two module systems side by side, and a file's extension or the nearest package.json settles which one it gets before a single line of it runs. 8 (all stubs)
15 Regular expressions A JavaScript regex carries state as well as a pattern: its flags change what . and \p{…} mean, and with g the same call can answer differently the second time. 7 (all stubs)
16 Dates and time A Date is one number of milliseconds since 1970 UTC, and nearly every date bug comes from converting to and from it: months counted from zero, strings read as UTC or as local time, and a zone chosen only when printing. 6 (all stubs)
17 Metaprogramming JavaScript lets a program change how the language treats its objects — with well-known symbols, proxies, template tags, decorators and using — and each hook comes with rules the engine enforces even when your code does not. 6 (all stubs)
18 Memory and garbage collection Node frees an object when nothing can reach it any more, not when you are done with it, so every leak is a reference you forgot, and collection is something a program can observe but never schedule. 4 (all stubs)

Where JavaScript runs

Node's own APIs, threads and processes, and the browser.

Chapter What it covers Pages
19 The Node.js runtime Node is the language plus an operating-system process, and most of its surprises are the operating system showing through: arguments and environment as strings, files as bytes, pipes that buffer, and exit statuses. 9 (all stubs)
20 Workers and parallelism One JavaScript agent never runs two things at once; parallelism in Node needs a second agent, a worker or a process, and data crosses between them by copy, by transfer, or through shared memory that needs Atomics. 5 (all stubs)
21 The browser Everything a page does beyond computing — its document, events, painting, network rules and storage — comes from the browser, not from JavaScript, and each part has rules that Node never enforces. 6 (all stubs)

TypeScript

The type system that is checked by tsc and erased before Node runs the code.

Chapter What it covers Pages
22 TypeScript basics TypeScript is checked by one program and run by another: tsc reads the types and reports errors, Node deletes the types and runs what is left, and nothing connects the two unless you run both. 8 (all stubs)
23 Everyday types Each everyday type names a set of values, and the surprises follow from that: a union of two object types lets you use fewer members, a readonly property guards only one path to the object, and a tuple can still push. 9 (all stubs)
24 Narrowing After a test that only some members of a union can pass, the checker shrinks the variable's type inside that branch; the chapter is about which tests it understands, which it takes on trust, and how to make it prove no case was forgotten. 6 (all stubs)
25 Type compatibility TypeScript decides assignability by shape, not by name: a value fits a type when it has the members the type asks for, and each page here is either a rule that follows from that or a way around it. 5 (all stubs)
26 Generics A type parameter is a blank that tsc fills in at every call from the arguments, and most surprises come from what it fills in when you did not say. 5 (all stubs)
27 Type operators Types can be computed from values and from other types, so one definition can produce the rest instead of being copied by hand and drifting. 7 (all stubs)
28 Classes in TypeScript Almost everything TypeScript adds to a class is checked by tsc and then erased, so each modifier is a compile-time promise, not a run-time wall. 5 (all stubs)
29 Declaration files and module resolution tsc checks your code against declarations it takes on trust and files it finds by its own rules, and either can disagree with what Node actually loads. 5 (all stubs)
30 Where types lie A program that tsc accepts under strict can still crash with a TypeError: five pages here show a way it happens, the clean type check beside the crash, and one shows the fix. 6 (all stubs)

Around both

The tools every project meets, and where to read further.

Chapter What it covers Pages
31 Tooling Each tool around a JavaScript project answers one question the others never check — which versions, which binary, which types, which mistakes, which layout, which syntax, which line runs next — and each can be swapped without touching the language. 8 (all stubs)
32 Resources Three ways to read further: the books on the shelf beside this library, mapped to its chapters; the official references its pages cite; and the same questions answered in the sibling libraries. 3 (3 reference)

Running the examples

You need Node 24 or later and Python 3 (for the runner). Node runs a .ts file itself by erasing its types; the TypeScript compiler that checks the types is pinned in package.json, so install it once:

npm ci
python3 tools/run_examples.py --check

Each example is one file, run from its own folder:

node page_check_total_js.js

The one rule

No page hand-types what a program prints. A lesson marks the spot and the runner fills it from a real run, so an example that behaves differently in a new Node or TypeScript release breaks the build instead of quietly making a page wrong. A stub has no output at all, and says so. See CONTRIBUTING.md.

Sibling libraries

The same questions, put to other languages. Stubs link the matching sibling page under See also.

  • Encodings ↗ — bytes, code points, UTF-16 and surrogate pairs, normalization: the ground under every JavaScript string.
  • Regex ↗ — one regular-expression question per page, with a JavaScript column beside Python, Perl, Java, Go, Rust and others.
  • Concurrency ↗ — event loops, async and await, workers and shared memory, across six languages.
  • Java text ↗ — Java's char is a UTF-16 code unit too, and fails the same way.
  • Math ↗ — machine numbers, the doubles every JavaScript number is.
  • Python ↗, Rust ↗, Go ↗, C ↗ and Linux ↗ — for side-by-side comparisons.