Programming TypeScript: Making Your JavaScript Applications Scale
Notes
What does “downlevel compiler” mean?
Typing Call
function call<T extends unknown[], U>(
f: (...args: T) => U,
...args: T
): U
function add(a: number, b: number): number {
return a + b
}
call(add, 1, 2) // 3
call(add, 1, "2") // type error: argument of type 'string' is not assignable to parameter of type 'number'.
call(add, 1) // type error:Expected 3 arguments, but got 2
call(add, 1, 2, 3) // type error Expected 3 arguments, but got 4
Typing Map
function map<T, U>(arr: T[], cb: (item: T, index: number, arr: T[]) => U): U[] {
const result = [];
for (let i = 0; i < arr.length; i++) {
result.push(cb(arr[i], i, arr));
}
return result
}
const myArray = [1,2,3];
const doubled = map(myArray, (num) => num * 2); // inferred result: number[]
const strings = map(myArray, (num) => num.toString()); // inferred result: string[]
Typing Mixins
type Constructor<T> = new (...args: any[]) => T
function withEzDebug<C extends Constructor<{getDebugValue: () => object}>>(
Class: C
) {
return class extends Class {
debug() {
const name = Class.name; // I believe the book is wrong here they had Class.constructor.name which returned Function
const value = this.getDebugValue();
return `${name}(${JSON.stringify(value)})`
}
}
}
class HardToDebugUser {
#id: number;
#firstName: string;
#lastName: string
constructor(id: number, firstName: string, lastName: string) {
this.#id = id;
this.#firstName = firstName;
this.#lastName = lastName;
}
getDebugValue() {
return {
id: this.#id,
name: `${this.#firstName} ${this.#lastName}`
}
}
}
const User = withEzDebug(HardToDebugUser);
let user = new User(1, "Tyler", "Hillery");
console.log(user.debug()); // "HardToDebugUser({"id":1,"name":"Tyler Hillery"})"
Review
Man I thought the author has lots of great content about TypeScript itself but I wasn’t the biggest fan their writing style. I appreciate them trying to make a technical book a more fun read by adding some humor but the jokes didn’t land for me. I also felt there many ways the author imposed their opinion rather than just describing the facts. Author said you should use ORMs in all places as raw sql is not type safe. Author easily could have shown the difference between using raw SQL vs ORM and let the reading come to conclusion if the benefits provided by the ORM are worth it (I have nothing against ORMs, just illustrating a point).
Another major section left out was the use of run time validation libraries like Zod that I think would have been a helpful addition. The author mentions using typed protocols like Swagger, gRPC etc. but what if you’re communicating with API that doesn’t implement one of these protocols? How can you go about validating at runtime that data you get is what you expect. I like doing this even when consuming things like OpenAPI specs because the server implementation can often be wrong.
With that said I thought the author showed some very examples, in particular I liked learning how to properly type an event emitter.
I still would recommend the book for those thing to get quickly up to speed with TypeScript.