App.tsxCourse

TypeScript and React

A typed name, then an object, a class, a copy before a change, a union of cases, and an interface that lists the methods.

A cup label is three typed values
  1. namestringlatte
  2. centsnumber450
  3. readybooleantrue

6 lessons, in orderFree to read

Syllabus

Basics

The types, explained, then an object you can name and call.

  1. A TypeScript name can carry a type. name is a string, cents is a number, and ready is a boolean. The program prints latte $4.50 ready.

    const name: string = "latte";
    const cents: number = 450;
    const ready: boolean = true;
    const status: string = ready ? "ready" : "sold out";
    const frac = String(cents % 100).padStart(2, "0");
    console.log(name + " $" + Math.floor(cents / 100) + "." + frac + " " + status);
    latte $4.50 ready
    
  2. An object type names the fields. Item has a drink and a price in cents. label takes an Item and the program prints latte $4.50.

    Builds on lesson 1.

    type Item = {
      name: string;
      cents: number;
    };
    
    function label(item: Item): string {
      const frac = String(item.cents % 100).padStart(2, "0");
      return item.name + " $" + Math.floor(item.cents / 100) + "." + frac;
    }
    
    const latte: Item = { name: "latte", cents: 450 };
    console.log(label(latte));
    latte $4.50
    

Practice

A rule that prevents a bug, then a program that uses the object.

  1. A class holds the fields and the method together. new Item makes a latte. label prints latte $4.50.

    Builds on lessons 1 through 2.

    class Item {
      name: string;
      cents: number;
    
      constructor(name: string, cents: number) {
        this.name = name;
        this.cents = cents;
      }
    
      label(): string {
        const frac = String(this.cents % 100).padStart(2, "0");
        return this.name + " $" + Math.floor(this.cents / 100) + "." + frac;
      }
    }
    
    const latte = new Item("latte", 450);
    console.log(latte.label());
    latte $4.50
    
  2. An object spread copies the fields and replaces one. The original latte stays 450 cents. The tipped copy is 540. The program prints 450 540.

    Builds on lessons 1 through 3.

    const item = { name: "latte", cents: 450 };
    const tipped = { ...item, cents: 540 };
    console.log(item.cents, tipped.cents);
    450 540
    

Further

One more idea, then a program that puts the pieces together.

  1. A union is a value that is one of several shapes. Paid is yes with the cents, or no. Reading yes prints paid 450. Reading no prints unpaid.

    Builds on lessons 1 through 4.

    type Paid = { kind: "yes"; cents: number } | { kind: "no" };
    
    function read(a: Paid): string {
      if (a.kind === "yes") return "paid " + a.cents;
      return "unpaid";
    }
    
    console.log(read({ kind: "yes", cents: 450 }));
    console.log(read({ kind: "no" }));
    paid 450
    unpaid
    
  2. An interface lists the methods a function needs. Labeller asks for label. Item has label. show prints latte $4.50.

    Builds on lessons 1 through 5.

    interface Labeller {
      label(): string;
    }
    
    class Item implements Labeller {
      name: string;
      cents: number;
    
      constructor(name: string, cents: number) {
        this.name = name;
        this.cents = cents;
      }
    
      label(): string {
        const frac = String(this.cents % 100).padStart(2, "0");
        return this.name + " $" + Math.floor(this.cents / 100) + "." + frac;
      }
    }
    
    function show(item: Labeller): void {
      console.log(item.label());
    }
    
    show(new Item("latte", 450));
    latte $4.50
    

Readings

Shorter pieces in the same language. Any order is fine once the lessons feel familiar.

  1. Make illegal states unrepresentable 2 min

    GoByte Skills #15: Three booleans are 8 states and your screen means three. Model React state as a tagged union and the compiler rules out the other 5. Validate the data at the edge, and the inside stays honest.

  2. State is a snapshot 2 min

    GoByte Skills #14: In React, count is not a live variable. Each render gets its own constant, and a click handler only ever sees the render it was born in. That is why setCount(count + 1) three times adds one, and how the updater fixes it.

  3. TypeScript narrowing: typeof is the bouncer 2 min

    GoByte Skills #13: typeof does not just check a type, it proves one. After the check each branch gets a narrower type and the compiler holds you to it. A handwritten type guard only promises.