Using is operator in typescript for type narrowing

Published: Updated:
3 min read 590 words
Banner

In TypeScript, the is keyword enables type predicates, which allow you to define custom type guards. These guards tell the compiler that when a function returns true, a value conforms to a specific type.

Type predicates are particularly useful when working with user input, API responses, or loosely typed data, where static analysis alone is not sufficient. They let you replace unsafe type assertions (as) with runtime-verified checks that TypeScript understands.


Step-by-Step Guide to Using Type Predicates

  1. Define the Predicate Function Declare a function with a return type in the format:

    parameterName is Type

    This signals to TypeScript that a true result guarantees the type.

  2. Implement Runtime Logic Perform runtime checks inside the function to validate the type. This may include:

    • property checks (in)
    • typeof
    • instanceof
    • discriminant comparisons
  3. Apply the Guard Use the function in conditionals (if, switch) or array methods like .filter(). Within the true branch, TypeScript narrows the type automatically.


Example: Validating Object Keys

const themeColors = {
  primary: "#007bff",
  secondary: "#6c757d",
} as const;

function isValidColorKey(key: string): key is keyof typeof themeColors {
  return key in themeColors;
}

const input = "primary";

if (isValidColorKey(input)) {
  // 'input' is narrowed to "primary" | "secondary"
  console.log(themeColors[input]);
}

Why this approach?

  • key in themeColors avoids the type widening issues of Object.keys()
  • It is more efficient at runtime
  • It aligns with TypeScript’s structural typing model

Example: Narrowing Union Types

A more common real-world use case is narrowing union types:

type Shape =
  | { type: "circle"; radius: number }
  | { type: "square"; size: number };

function isCircle(shape: Shape): shape is Extract<Shape, { type: "circle" }> {
  return shape.type === "circle";
}

function getArea(shape: Shape) {
  if (isCircle(shape)) {
    return Math.PI * shape.radius ** 2;
  }

  return shape.size ** 2;
}

This pattern is especially useful when:

  • working with complex unions
  • encapsulating reusable narrowing logic
  • simplifying conditional branches

Using Type Predicates with .filter()

Type predicates are highly effective with array filtering:

const values = [1, null, 2, undefined, 3];

function isNumber(value: number | null | undefined): value is number {
  return value != null;
}

const numbers = values.filter(isNumber);
// inferred as number[]

Without a type predicate, TypeScript would not correctly infer the filtered result.


When You Don’t Need is

Type predicates are powerful, but not always necessary.

If you’re working with a discriminated union, TypeScript can often narrow types automatically:

if (shape.type === "circle") {
  // already narrowed to circle
}

Use custom type predicates when:

  • narrowing logic is reused
  • the check is non-trivial
  • TypeScript cannot infer the narrowing on its own

Important Considerations

  • TypeScript Trusts You The compiler does not verify your logic. If your predicate returns true incorrectly, TypeScript will still narrow the type, which can lead to runtime errors.
  • Prefer Runtime-Accurate Checks Ensure your checks reflect actual runtime behavior (in, typeof, etc.), not just what “seems correct.”
  • Best Use Cases
    • narrowing unknown or any
    • refining API responses
    • filtering nullable values
    • handling complex unions

Advanced: Assertion Functions

In some cases, you may want to enforce correctness instead of branching:

function assertIsString(value: unknown): asserts value is string {
  if (typeof value !== "string") {
    throw new Error("Not a string");
  }
}

Unlike type predicates:

  • is → narrows within a conditional
  • asserts → throws or guarantees the type afterward

References

Further reading
previous
Understanding the JavaScript event loop
Understanding the JavaScript event loop
next
Using React Hook Form with Material UI
Using React Hook Form with Material UI