Feb. 20, 2022, noon

PinkLetter - TYPEScript

PinkLetter (odone.io)

Welcome to my PinkLetter. A short, weekly, technology-agnostic, and pink newsletter where we cultivate timeless skills about web development.

My Ramblings This Week

TypeScript. Oh, TypeScript.

If you follow me on Twitter, you are aware I’ve been playing with its type system.

You can picture TypeScript as two languages in one: JavaScript (with a few additions) and the type language.

Here’s one tweet thread about refactoring from unsafe JavaScript to typesafe TypeScript. It shows some cool things you can do with a powerful type system:

app.get("/api/users/:userID", function(req, res) {
  if (req.method === "POST") { // always false
    res.status(20).send({ // 20?!
      message:
        "Got you, user " + req.params.userId // should be userID
    });
  }
})

//Let's add some basic types
declare const app: App;
type App = {
  get(x: string, y: (z: Req, w: Res) => void): void
}
type Req = {
  method: string,
  params: Record<string, string>
}
type Res = {
  status(x: number): StatusReturn
}
type StatusReturn = {
  send(x: Record<string, string>): void
}
app.get("/api/users/:userID", function(req, res) {
  if (req.method === "POST") {
    res.status(20).send({
      message:
        "Got you, user " + req.params.userId
    });
  }
})

//Let's prevent invalid statuses
declare const app: App;
type App = {
  get(x: string, y: (z: Req, w: Res) => void): void
}
type Req = {
  method: string,
  params: Record<string, string>
}
type Res = {
  status(x: Status): StatusReturn // 👈
}
type Status = 200 | 201 // || ... 👈
type StatusReturn = {
  send(x: Record<string, string>): void
}
app.get("/api/users/:userID", function(req, res) {
  if (req.method === "POST") {
    res.status(20).send({ // ✅ Does not compile now
      message:
        "Got you, user " + req.params.userId
    });
  }
})

//Let's derive req.method
declare const app: App;
type App = {
  get(x: string, y: (z: Req<'GET'>, w: Res) => void): void // 👈
}
type Req<Method> = { // 👈
  method: Method, // 👈
  params: Record<string, string>
}
type Res = {
  status(x: Status): StatusReturn
}
type Status = 200 | 201 // || ... 
type StatusReturn = {
  send(x: Record<string, string>): void
}
app.get("/api/users/:userID", function(req, res) {
  if (req.method === "POST") { // ✅ Does not compile now
    res.status(200).send({
      message:
        "Got you, user " + req.params.userId
    });
  }
})

//Let's derive req.params from the pattern
declare const app: App;
type App = {
  get<Pattern extends string>(x: Pattern, y: (z: Req<'GET', Pattern>, w: Res) => void): void // 👈
}
type Req<Method, Pattern> = {
  method: Method,
  params: ParamsFromPattern<Pattern> // 👈 
}
type ParamsFromPattern<Pattern> = // 👈
  Pattern extends ${infer _Ignore}/:${infer Param} ? 
    Record<Param, string> :
    never
app.get("/api/users/:userID", function(req, res) {
  res.status(200).send({
    message:
      "Got you, user " + req.params.userId // ✅ Does not compile now; should be userID
  });
})

Elsewhere on the Web

What the Version aka WTV? by yours truly

Version ranges in different languages: Ruby, JavaScript, Rust.

It’s clear what >, >=, <, or <= mean. But what about those other cryptic symbols?


Make Data Structures by Richard Feldman

Start by thinking about what data structures I’m going to use to represent my application.


Text Aesthetics: Command Line UI/UX by Michael Cordell

In this post I’ll show the many ways you can customize your command line. Through this customization, you can have a more pleasant and efficient experience while retaining the power and flexibility of text-only software. We will start with the terminal emulator, the main window to your CLI. We’ll move on to the text and colors that style the interface. We’ll close with using prompts and status lines to bring context to your text interface.

You just read issue #86 of PinkLetter (odone.io). You can also browse the full archives of this newsletter.

Brought to you by Buttondown, the easiest way to start and grow your newsletter.