Type Guards & Assertion Functions
10 min read
Type Guards & Assertion Functions
A user-defined type guard is a function whose return type is a type predicate (value is T). When the function returns true, TypeScript narrows the argument to type T in the surrounding scope.
ts
interface Circle {
kind: "circle";
radius: number;
}
interface Rectangle {
kind: "rect";
width: number;
height: number;
}
type Shape = Circle | Rectangle;
function isCircle(shape: Shape): shape is Circle {
return shape.kind === "circle";
}
function describeShape(shape: Shape): string {
if (isCircle(shape)) {
return `Circle with radius ${shape.radius}`; // shape: Circle here
}
return `Rect ${shape.width}x${shape.height}`; // shape: Rectangle here
}Assertion functions
Assertion functions use asserts value is T (or just asserts condition) as the return type. If the function returns normally (without throwing), TypeScript treats the assertion as proven for the remainder of the scope.
ts
function assertIsString(value: unknown): asserts value is string {
if (typeof value !== "string") {
throw new TypeError(`Expected string, got ${typeof value}`);
}
}
function assertDefined<T>(value: T | null | undefined): asserts value is T {
if (value == null) {
throw new Error("Value must not be null or undefined");
}
}
function processInput(raw: unknown) {
assertIsString(raw);
// raw is now narrowed to string
console.log(raw.toUpperCase());
}Type predicates and assertion functions are promises to the compiler — TypeScript trusts them completely. An incorrect predicate that returns true for the wrong type will silently introduce runtime errors. Always write thorough runtime checks inside them.
Ready to test yourself?
Practice TypeScript— quiz & coding exercises