Menu

TypeScript Type Guard: Check the Type of an Object

A type guard is a runtime check that TypeScript understands. Learn the built-in guards, how to write your own with a value is Type predicate, how to check if an object is of a type, assertion functions with asserts, and how to validate unknown data.

This page includes runnable editors - edit, run, and see output instantly.

A type guard is a runtime check that TypeScript understands, so it narrows the type inside the checked branch. typeof, instanceof and in are built-in guards; for anything else, you write a function whose return type is a type predicate, value is Type.

isUser returns a plain boolean at run time. The value is User return type tells the compiler what a true result proves, and every if (isUser(x)) then narrows x to User.

Built-in Type Guards

These checks narrow without any helper function:

GuardExampleUse it for
typeoftypeof x === "number"primitives and functions
instanceofx instanceof Dateclass instances
in"email" in xobject unions, properties of unknown objects
Array.isArrayArray.isArray(x)arrays
Equalityx === null, x.kind === "circle"null/undefined, literal tags
Truthinessif (x)removing null and undefined

They all run as ordinary JavaScript. What TypeScript adds is the narrowing: it reads the check and adjusts the type in each branch. The full list of forms is on the type narrowing page. A custom guard is for checks that do not fit in one expression, or that you want to reuse.

Writing a Type Predicate

A type predicate has the form parameterName is Type and replaces boolean as the return type. The narrowing works in both directions: true narrows to Type, and false removes Type from a union.

Passing a guard to filter gives a correctly typed array. Since TypeScript 5.5 the compiler also infers a predicate from simple arrow functions, so pets.filter((p) => p.kind === "cat") returns Cat[] without a named guard.

The predicate's type must fit the parameter's type: function f(x: string): x is number is error TS2677, A type predicate's type must be assignable to its parameter's type.

The Compiler Trusts Your Guard

TypeScript checks that a guard returns a boolean. It does not check that the boolean is right. A guard that returns true for the wrong values makes the types lie, and the program fails at run time with no compile error.

data.price.toFixed(2) throws TypeError: Cannot read properties of undefined (reading 'toFixed') at run time. The compiler accepted data.price as a number because the guard said so. Check every property the rest of the code relies on, and keep guards small, tested and close to the type they describe.

Checking If an Object Is of a Type

This is the question behind most custom guards: data arrives as unknown (from JSON.parse, fetch, localStorage, a message) and you need to know whether it matches your interface. The recipe:

  1. typeof value === "object" && value !== null (an object, not null).
  2. "prop" in value for each required property. On unknown, in adds the property to the type as unknown.
  3. typeof value.prop === "..." (or a nested guard) for each property's type.
  4. Array.isArray(value.items) && value.items.every(isItem) for arrays.

For large or deeply nested shapes, writing these by hand gets tedious. Schema libraries such as Zod or Valibot let you describe the shape once and give you both the runtime check and the TypeScript type.

Assertion Functions: asserts value is Type

An assertion function throws if the check fails and returns normally otherwise. Its return type is asserts value is Type (or asserts condition), and everything after the call is narrowed, with no if needed.

One rule trips people up: an assertion function must be called through a name with an explicit type. A const arrow function without an annotation, const check = (v: unknown): asserts v is string => {...}, gives error TS2775 at the call site, Assertions require every name in the call target to be declared with an explicit type annotation. Use a function declaration, or annotate the constant with a function type.

Guards vs Assertions vs Casts

ToolRuntime check?NarrowsOn failure
Built-in guard (typeof, in...)Yesinside the branchtakes the other branch
value is T functionYes (your code)inside the branchtakes the other branch
asserts value is T functionYes (your code)after the callthrows
value as TNothe expressionnothing: the wrong type spreads

A type assertion (as) changes the type without checking anything. At a boundary where data comes from outside, a guard or an assertion function is the safe version of the same idea.

this-Based Guards in Classes

A method can narrow the object it is called on with this is Type. It is handy in class hierarchies:

class FileNode {
  constructor(public name: string) {}
  isDirectory(): this is DirectoryNode {
    return this instanceof DirectoryNode;
  }
}

class DirectoryNode extends FileNode {
  children: FileNode[] = [];
}

function count(node: FileNode): number {
  return node.isDirectory() ? node.children.length : 0; // node: DirectoryNode in the true branch
}

Frequently Asked Questions

What is a type guard in TypeScript?

Any runtime check that TypeScript uses to narrow a type: typeof x === "string", x instanceof Date, "id" in x, Array.isArray(x), or a call to a function whose return type is a type predicate such as x is User. Inside the checked branch the variable has the narrower type.

How do I check if an object is of a type in TypeScript?

Types do not exist at run time, so you check the properties: write a function isUser(value: unknown): value is User that tests typeof value === "object", value !== null, and each required property with in and typeof. After if (isUser(x)), x is typed User. For classes, x instanceof MyClass is enough.

What does "value is Type" mean in TypeScript?

It is a type predicate, used as a function's return type. The function still returns a boolean at run time, but when it returns true TypeScript narrows the argument to Type at the call site, and when it returns false it narrows it to the other members of the union. The compiler does not verify the function body, so the check must be correct.

What is the difference between a type guard and an assertion function?

A type guard (x is T) returns a boolean and narrows inside an if. An assertion function (asserts x is T) returns nothing and throws when the check fails, so everything after the call is narrowed without an if. Use guards for branching and assertions for "this must hold, otherwise stop".

Can I check if an object implements an interface in TypeScript?

Not directly: interfaces are erased and instanceof does not accept them. Write a type guard that checks the interface's properties, or add a literal tag property (kind: "user") and compare it. Schema libraries such as Zod generate both the check and the type from one definition.

Coddy programming languages illustration

Learn to code with Coddy

GET STARTED