TypeScript Tutorial: Type Guards & Narrowing
Safely narrow union types at runtime using typeof, instanceof, in, and custom type guards.
What is Narrowing?
TypeScript starts wide (a union type) and narrows based on runtime checks:
```typescript
function process(input: string | number) {
// Here: input is string | number
if (typeof input === "string") {
// Here: input is string ← narrowed
console.log(input.toUpperCase());
} else {
// Here: input is number ← narrowed
console.log(input.toFixed(2));
}
}
```
---
typeof Guard
```typescript
type Primitive = string | number | boolean;
function describe(val: Primitive): string {
if (typeof val === "string") return `"${val}"`;
if (typeof val === "number") return val.toFixed(2);
return val ? "true" : "false";
}
```
---
instanceof Guard
```typescript
class Cat { meow() { return "Meow!"; } }
class Dog { bark() { return "Woof!"; } }
function makeNoise(animal: Cat | Dog): string {
if (animal instanceof Cat) return animal.meow();
return animal.bark();
}
```
---
in Guard
Check if a property exists on an object:
```typescript
interface Circle { kind: "circle"; radius: number }
interface Square { kind: "square"; side: number }
function area(shape: Circle | Square): number {
if ("radius" in shape) return Math.PI * shape.radius ** 2;
return shape.side ** 2;
}
```
---
Custom Type Guard Functions
Return `value is Type` to create reusable narrowing functions:
```typescript
interface Fish { swim(): void }
interface Bird { fly(): void }
function isFish(animal: Fish | Bird): animal is Fish {
return "swim" in animal;
}
function move(animal: Fish | Bird) {
if (isFish(animal)) {
animal.swim(); // TypeScript knows it's Fish here
} else {
animal.fly(); // TypeScript knows it's Bird here
}
}
```
---
Exhaustive Checks with never
Use `never` to ensure you've handled all union members:
```typescript
type Shape = "circle" | "square" | "triangle";
function describe(s: Shape): string {
switch (s) {
case "circle": return "round";
case "square": return "4 equal sides";
case "triangle": return "3 sides";
default:
const exhausted: never = s; // Error if a case is missing
return exhausted;
}
}
```
---
What's Next?
Next: **Async/Await** — typing async functions and Promises in TypeScript.
What you'll learn in this TypeScript type guards & narrowing tutorial
This interactive TypeScript tutorial has 13 hands-on exercises. Estimated time: 12 minutes.
- typeof — narrow primitives — The `typeof` operator narrows the type of a value inside a conditional block. TypeScript uses this to know exactly what …
- instanceof — narrow class instances — `instanceof` checks if a value was created by a specific constructor. TypeScript narrows to that class inside the block.
- in operator — check for property existence — The `in` operator checks if a property exists on an object. TypeScript narrows the type based on the property check.
- Custom type guard — is keyword — A type guard function with `value is Type` as the return type tells TypeScript to narrow the type at the call site when …
- Assertion functions — throw or guarantee — An assertion function throws if a condition isn't met. After calling it, TypeScript knows the condition holds.
- Discriminated union narrowing — A discriminated union uses a shared literal field (`kind`, `type`, `status`) as a discriminant. TypeScript narrows autom…
- Narrowing with equality checks — TypeScript narrows types based on equality comparisons. Checking `x === null` narrows `x` to `null` in that branch, and …
- Control flow analysis — unreachable code — TypeScript performs control flow analysis — it tracks which values are possible at each point in the code. This lets it …
- Narrowing with Array.isArray — `Array.isArray()` is a built-in type guard — TypeScript narrows to `T[]` when it returns true.
- Never — exhaustive union handling — `never` is the empty type — a value that should never exist. Use it in the `default` case of a switch to make TypeScript…
- Type narrowing in callbacks — Type narrowing works inside callbacks too. TypeScript tracks the narrowed type through function boundaries when the narr…
- Narrowing with truthiness — null and undefined — Checking a value's truthiness narrows away `null`, `undefined`, `0`, `""`, and `false`. TypeScript understands all truth…
- Using satisfies for narrowed object types — `satisfies` validates an expression matches a type while preserving the narrowest possible inferred type. Use it when yo…
TypeScript Type Guards & Narrowing concepts covered
- What is Narrowing?
- typeof Guard
- instanceof Guard
- in Guard
- Custom Type Guard Functions
- Exhaustive Checks with never
- What's Next?