P.5 · JavaScript/TypeScript I: The Language

P.5 · 75 min

JavaScript/TypeScript I: The Language

Soon, agents will write most of your JavaScript. Which raises the obvious question: why learn it? Because you cannot supervise what you cannot read. An agent that produces 300 lines of TypeScript is handing you 300 lines of claims, and reviewing claims in a language you do not read is not reviewing, it is vibes with extra steps. You do not need to become a fluent writer. You need to be a strong reader and a capable-enough writer that nothing on the page is a mystery. That bar is reachable this week.

Setup: node and npm

JavaScript runs in two places: browsers, and everywhere else via Node.js, a program that executes JS files on your machine. Install the LTS version (LTS means long-term support, the stable line meant for real work; as of July 2026 that is Node 24, but take whatever the site marks LTS). Go to nodejs.org/en/download and follow the instructions for your platform; on Windows, install it inside WSL. Then verify in your terminal:

node --version
npm --version

Both should print version numbers. npm came along for free: it is the package manager, the tool that downloads libraries other people wrote into your project. You will meet it properly at the end of this lesson.

The fastest feedback loop you own is now:

node -e "console.log(1 + 2)"

Or run node alone to get an interactive prompt where each line evaluates as you type it (ctrl+d exits). Every claim this lesson makes, you can check in five seconds. That habit, check the claim in the REPL, is worth more than the lesson.

The map: the language in one sitting

Values and types. JavaScript has a small set of value kinds: numbers (42, 3.5, no separate integer type), strings ("Dana" or 'Dana' or, with embedded values, `Hello ${name}`), booleans (true, false), plus two flavors of nothing: null (deliberately empty) and undefined (never set). Variables are declared with const (cannot be reassigned, use it by default) or let (can be, use it only when a value genuinely changes).

Objects and arrays. The two shapes all data takes. An object is labeled compartments; an array is an ordered list. They nest freely, and JSON from last lesson is exactly this, written down:

const contact = {
  name: "Dana Whitfield",
  status: "hot",
  daysSinceContact: 12,
  tags: ["vip", "demo-booked"],
};

console.log(contact.name);        // "Dana Whitfield"
console.log(contact.tags[0]);     // "vip" (arrays count from zero)

const { status, name } = contact; // destructuring: pull fields into variables

That last line, destructuring, is everywhere in modern code and reads stranger than it is: "make variables named status and name from the matching fields of contact."

Functions. Reusable behavior with a name. Two syntaxes, one idea:

function isStale(contact) {
  return contact.daysSinceContact > 7;
}

const isStale2 = (contact) => contact.daysSinceContact > 7;

The arrow form on the second line dominates modern code, especially as an inline argument to another function. When the body is a single expression, it returns that expression implicitly. Read (x) => x * 2 as "a tiny nameless function that doubles its input."

Control flow. if/else you can already read. Loops:

for (const tag of contact.tags) {
  console.log(tag);
}

But here is an opinion the rest of the course relies on: in modern JS, most loops over data are written as array methods instead. leads.filter(isStale) returns the stale ones. leads.map((l) => l.name) transforms each element. These read as sentences and compose like the terminal pipes from P.2. Lesson P.6 drills them hard; for now, just recognize the shape: array.method(functionToApplyToEachElement).

Modules. Code is split across files. A file exports what it offers, another imports it:

// stale.js
export function isStale(contact) {
  return contact.daysSinceContact > 7;
}

// report.js
import { isStale } from "./stale.js";

Local files get a ./ path; library names alone (import chalk from "chalk") come from node_modules, the folder npm installs into.

TypeScript: types as documentation

TypeScript is JavaScript plus annotations saying what type each thing is. It compiles to plain JS; the annotations exist for the compiler and the reader, and both matter to you.

type Contact = {
  name: string;
  status: "hot" | "warm" | "cold";
  daysSinceContact: number;
  tags: string[];
};

function isStale(contact: Contact): boolean {
  return contact.daysSinceContact > 7;
}

Read the annotations as enforced documentation. The Contact type tells you the exact shape of the data, and status cannot be anything except those three strings; misspell "hto" anywhere and the compiler rejects it before the code runs. isStale declares it takes a Contact and returns a boolean, and the compiler holds it to that. This is why the course prefers TypeScript everywhere: when an agent writes 300 lines, the types are the part you can verify at a glance, and the compiler is a free reviewer that never gets tired. A whole class of bugs (wrong field name, wrong argument order, missing value) dies at compile time instead of in front of a customer.

You do not need to write sophisticated types yet. You need to read them, and to respect the compiler error instead of fighting it. The error is almost always pointing at a real confusion about your data.

Tutor first, then drills

PromptJS/TS drill tutor

You are my JavaScript tutor. Drill me with prediction questions: show me 3 to 6 lines of simple JS or TS and ask what it prints or whether it compiles, one at a time. I will answer, then run it with node to check. Cover: const/let, objects and arrays, destructuring, arrow functions, filter and map, template strings, and simple type annotations. Escalate when I am right, come at the same idea differently when I am wrong. No trick questions about obscure corners: stick to code a working developer would actually write. After 10, tell me what to drill tomorrow.

Run that for twenty minutes before the lab. Predict first, then verify with node -e or the REPL. The gap between your prediction and node's answer is your syllabus.

The lab: build a CLI, no codegen

Lablab-p-5Lead report CLI

Goal: Write a small command-line utility yourself, from a spec, with Claude as tutor only: no generated code.

The spec. Build report.js, run as node report.js, which:

  1. Holds an array of at least five lead objects, each with name (string), status (one of "hot", "warm", "cold"), and daysSinceContact (number).
  2. Prints one line per hot lead: FOLLOW UP: <name> (<daysSinceContact> days) for hot leads not contacted in more than 7 days, and ok: <name> for the other hot leads.
  3. Ends with a summary line: <n> hot leads, <m> need follow-up.

Rules of engagement: you type every character. Claude may explain concepts, review your code after you attempt each step, and answer questions. It may not write the code. Use this contract:

PromptTutor-only contract

I am building a small CLI from a spec as a learning exercise. Your job is tutor, not builder. Hard rule: never write or complete my code, even if I ask casually. If I paste code, review it: point at problems and ask me questions that lead me to the fix. If I am stuck, give me the smallest possible hint (a concept name or a question), not a solution. I will tell you the spec and show you my attempts as I go.

Suggested path, one runnable step at a time:

  1. Create the file with the array and print all names with a loop or for...of. Run it. Working? Commit the moment (mentally or with git; you have a repo habit now).
  2. Add the filter for hot leads. leads.filter((l) => l.status === "hot"). Print those names only. Run it.
  3. Add the follow-up condition and the two message formats. Template strings (`FOLLOW UP: ${l.name}`) keep it readable. Run it.
  4. Add the summary line. You need two counts; .length on your filtered arrays has them.
  5. Read your own finished program top to bottom, out loud, once. If any line requires faith rather than understanding, ask your tutor about that line.

Verify

  • node report.js runs without errors and the output matches the spec formats exactly, including the summary counts being correct for your data.
  • Change one lead's daysSinceContact from 3 to 30 and rerun: the output changes accordingly. If it does not, your filter and your print logic disagree somewhere.
  • You can explain every line to Claude in explain-back mode and survive the harsh grading.
>Troubleshooting
  • SyntaxError: Unexpected token. Node is pointing at the first place the punctuation stopped making sense, which is usually a missing closing brace, bracket, or quote slightly above the reported line. Count your pairs.
  • undefined printing where a name should be. You are accessing a field that does not exist, often a typo like l.Name for l.name. JavaScript does not error on missing fields; it hands you undefined and lets you find out downstream. (TypeScript would have caught this, which is the sales pitch.)
  • Your import/export attempt fails with a module error. For a single-file script you do not need modules at all. Keep this lab in one plain JS file; multi-file imports get proper treatment in P.6.

Where it breaks

The honest failure modes of learning a language in the agent era. First, the fluency mirage: after a week of reading agent output, JS looks familiar, and familiarity impersonates competence. The lab above is the antidote, and the prove-it protocol from P.1 applies: if you cannot write the filter line cold, you cannot verify an agent's filter line either. Second, skipping to frameworks: React, Next.js, and friends sit on top of this lesson's material, and beginners who jump straight there drown in three layers of abstraction at once. The course sequences them deliberately; trust the sequence. Third, fighting the type checker: when TypeScript rejects your code, beginners reach for any (the type that means "stop checking me") or ask the agent to silence the error. The error is usually a fact about your data you have not absorbed yet. Read it, ask your tutor to translate it, fix the actual mismatch.

Knowledge check

Knowledge check

Q1What does this print? const leads = [{name: 'A', hot: true}, {name: 'B', hot: false}]; console.log(leads.filter((l) => l.hot).length);
Q2An agent wrote: function applyDiscount(order: Order, pct: number): Order. Without reading the body, what do you already know?
Q3Your script crashes: TypeError: Cannot read properties of undefined (reading 'length') at line 12, which reads const n = lead.tags.length;. What happened?
Q4During the lab you get stuck and type 'ok just write the filter line for me.' Claude, under your tutor-only contract, should do what, and why does it matter?

Sources

The language semantics taught here are stable ECMAScript features, all verifiable locally in your REPL, which beats any citation.