Type Guards & Narrowing
typeof Type Guard
function isNumber(x: any): x is number {
return typeof x === "number";
}
function process(val: string | number) {
if (typeof val === "string") {
console.log(val.toUpperCase());
} else {
console.log(val.toFixed(2));
}
}
instanceof Type Guard
class Dog { bark() {} }
class Cat { meow() {} }
function animalSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
in Operator Narrowing
interface Bird { fly: () => void; }
interface Fish { swim: () => void; }
function move(animal: Bird | Fish) {
if ("fly" in animal) {
animal.fly();
} else {
animal.swim();
}
}
Equality Narrowing
function print(val: string | null) {
if (val === null) return;
console.log(val.toUpperCase()); // val is string
}
Truthiness Narrowing
function printName(name?: string) {
if (name) {
console.log("Hello " + name);
}
}
Custom Type Predicate
interface User { name: string; }
function isUser(obj: any): obj is User {
return obj && typeof obj.name === "string";
}
if (isUser(someData)) {
console.log(someData.name);
}
Discriminated Unions
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function getArea(s: Shape) {
if (s.kind === "circle") {
return Math.PI * s.radius ** 2;
} else {
return s.side ** 2;
}
}
Control Flow Analysis
function example(x: string | number) {
if (typeof x === "string") return;
// x is number here
console.log(x.toFixed());
}
Assertion Functions
function assertIsString(val: any): asserts val is string {
if (typeof val !== "string") throw new Error("Not a string");
}
const val: any = "hello";
assertIsString(val);
console.log(val.toUpperCase());
Nullish Coalescing (??)
const input = null;
const value = input ?? "default"; // "default"
Optional Chaining (?.)
const user = { details: { age: 30 } };
const age = user?.details?.age;