Capítulo 3 de 36
TypeScript overlays type analysis on top of JavaScript's own runtime control-flow constructs (if/else, switch, ternaries, loops, truthiness, assignments) — every check you'd naturally write in JS to distinguish values also narrows the static type, no special syntax required.
typeof type guards: typeof x === "number" narrows x inside that branch. Values: "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function". Gotcha: typeof null === "object", so a typeof x === "object" check does not exclude null.if (x) coerces via JS truthiness rules (falsy: 0, NaN, "", 0n, null, undefined). Convenient for filtering out null/undefined, but risky on primitives — if (strs) also filters out a legitimate empty string, which is easy to do by accident.===, !==, ==, !=, switch): comparing two union-typed values narrows both to their common type. Comparing against == null narrows out both null and undefined at once (JS's loose-equality quirk, and TS understands it).in operator narrowing: "prop" in x narrows a union to the member(s) that have that property (optional properties count as present on both branches).instanceof narrowing: x instanceof Date narrows based on the prototype chain — the standard guard for class instances.string | number still lets you assign either a string or a number back after narrowing to one).return/throw removes that case from the type for the rest of the function, and type can differ at every point along diverging/re-merging branches.param is Type (e.g. function isFish(pet: Fish | Bird): pet is Fish) — calling it narrows the argument in the if/else branches, and works as an Array.prototype.filter predicate to narrow an array's element type.asserts x is T) for narrowing via a function that throws instead of returning a boolean — mentioned only in passing in the handbook page itself, full treatment is elsewhere.kind: "circle" vs kind: "square"), each with its own required (not optional) properties. Checking the discriminant (if/switch on shape.kind) then narrows to the exact member — far more reliable than optional properties + non-null assertions on one flat interface.never and exhaustiveness checking: narrowing every member out of a union leaves never (a type nothing can occupy, but which is itself assignable to anything). Assigning the leftover value to a never-typed variable in a switch's default case makes the compiler flag it if a new union member is ever added and left unhandled — turns "did I handle every case?" into a compile error instead of a runtime surprise.interface Circle { kind: "circle"; radius: number }
interface Square { kind: "square"; sideLength: number }
type Shape = Circle | Square;
function getArea(shape: Shape) {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2; // narrowed to Circle
case "square": return shape.sideLength ** 2; // narrowed to Square
default:
const _exhaustiveCheck: never = shape; // compile error if a member is added and unhandled
return _exhaustiveCheck;
}
}
| Narrowing technique | Trigger | Narrows on |
|---|---|---|
typeof | typeof x === "string" | primitive kind (note: typeof null === "object") |
| Truthiness | if (x) / !x | filters falsy values (0, "", NaN, 0n, null, undefined) |
| Equality | ===/==/switch | literal values or common type between two unions; == null catches both null and undefined |
in | "prop" in x | union members that declare prop |
instanceof | x instanceof Class | prototype chain membership |
| Type predicate | function f(x): x is T | whatever the predicate asserts, incl. through .filter() |
| Discriminant check | x.kind === "circle" | union member sharing that literal-typed field |
kind/type field, required — not optional — per-member properties) instead of one interface with a pile of optional fields; it eliminates most non-null assertions.never-assignment trick in a switch default to get a compile-time guarantee that every union member is handled — critical when a union is likely to grow.if (strs)) on values that could legitimately be "" or 0 — they silently swallow the valid-but-falsy case.== null / != null is a deliberate, TypeScript-aware idiom for "not null and not undefined" — prefer it over two separate strict checks when you want to exclude both.this is Type narrowing for methods, an extension of the type-predicate mechanism.