TypeScript Survival Guide

Favorite typescript videos

043 — Type Narrowing with Type Guards, Control Flow and More (typeof to predicates)

Full companion narrative for Mostly Code on type narrowing: typeof, truthiness, Array.isArray, in, instanceof, type predicates, and the never else-branch

Video: 043 · Learn TypeScript: Type narrowing with Type Guards, Control flow and much more! · watch · ~14:30 · Mostly Code

Source: this is a written companion to the video. Part of the Favorite TypeScript Videos curated path, module 02 (type system). The survey lesson: JavaScript-built-in guards (typeof, truthiness, Array.isArray, in, instanceof) into TypeScript-level guards (predicates, discriminated unions).

the union problem

A function takes string | number, and before any guard the editor only offers members common to both types. The fix is typeof narrowing — the first rung of the full JavaScript-built-in ladder this video walks.

typeof narrows primitives (and nothing else)

function myFunction(input: string | number) {
	if (typeof input === "string") {
		return input.replaceAll(" ", "_"); // string methods
	} else {
		return input.toPrecision(2); // number methods only
	}
}

myFunction typeof demo

typeof branch detail

else-branch number methods

typeof null is "object" — use truthiness for nullable unions

typeof null returns "object", so a { item: string | null } | null parameter cannot be split with typeof alone. The video guards with a falsy check first (if (input && typeof input === "object")), since null is falsy.

Array.isArray for string | string[]

function f(input: string | string[]) {
	if (Array.isArray(input)) {
		return input.map((i) => i); // array methods
	} else {
		return "hello"; // plain string
	}
}

Array.isArray branch

in operator — key presence, even with undefined/null values

"surname" in myObject is false (absent key). "address" in myObject is true even though the value is undefined; "second_name" in myObject is true even though the value is null. Presence, not definedness. Then narrows myObject.address with ?? "" once presence is established.

instanceof for class instances (Date vs String object vs string)

input instanceof Date leads to toUTCString(); input instanceof String leads to boxed-string methods; the final else is the primitive string. Call sites: new Date(), new String("hello"), "hello".

typeof plus instanceof

predicates plus a name-discriminated union close the video

Glass (name: "glass", capacityMl) and ExteriorWindow (name: "exteriorWindow", doubleGlazing) share a base interface. const isGlass = (o: Glass | ExteriorWindow): o is Glass => o.name === "glass" narrows the consumer; the unreached final else infers never.

Covered: typeof narrowing for primitive unions, with else-branch inference; typeof null === "object" trap, fixed with a truthiness pre-check; Array.isArray, in (presence vs definedness), instanceof across the ladder; type predicates over a shared name field, never in the unreached else. Code frames embedded above: frame-0002.jpg–frame-0004.jpg (myFunction typeof demo, [00:01:48]–[00:04:13]); frame-0005.jpg (input.map(f => f) array branch, [00:05:26]); frame-0010.jpg (typeof + instanceof, [00:11:28]). Remaining frames (frame-0001.jpg, frame-0006.jpg–frame-0009.jpg, frame-0011.jpg, frame-0012.jpg) are title cards and transitional shots. Transcript notes: The auto-transcript mishears several terms; this narrative uses the corrected forms: type narrowing (not “tap narrowing”), type string / TypeScript (not “tab string/tab script”), falsy value (not “false value”), nullish coalescing (??, not “new local operator”), and a function taking Date | String | string (not “my object function”).

Prev: 042 — The any Type in TypeScript · Next: 044 — Type Narrowing in TypeScript