Skip to content
</>CodeAndBuild

TypeScript

Narrow types at the edge of your program

Validate untrusted input once, where it enters, and let the rest of the function accept a type it can trust.

7 min read
  • TypeScript
  • Validation
On this page
  1. Write a guard that returns a type
  2. Trust the value after the parse

unknown is the honest type for data you did not create: a request body, a query string, a file on disk. The mistake is to cast it with as in the middle of a function and hope the shape holds.

Write a guard that returns a type

A type predicate narrows unknown to the shape you checked. Call it at the edge. The code after it can stop asking whether the field exists.

lib/guide.tsts
type GuideInput = {
  title: string;
  minutes: number;
};

function isGuideInput(value: unknown): value is GuideInput {
  if (typeof value !== "object" || value === null) return false;

  const record = value as Record<string, unknown>;
  return (
    typeof record.title === "string" &&
    typeof record.minutes === "number"
  );
}

export function parseGuide(value: unknown): GuideInput {
  if (!isGuideInput(value)) {
    throw new Error("Guide payload is missing a title or minutes.");
  }

  return value;
}

The cast inside the guard is local, and it sits behind an object check. That is a different habit from sprinkling as GuideInput through the UI.

Trust the value after the parse

parseGuide either throws or returns GuideInput. Callers should not re-check title. If a field is optional, model that in the type and handle it inside the parser, not in every consumer.

Forms and search params

searchParams values arrive as string | string[] | undefined. Normalize them in one function that returns a small object. Components then receive page: number, not an array they each parse differently.

  1. 01

    Accept the raw value at the boundary

    The parameter type is unknown, or the framework type you have not checked yet.

  2. 02

    Return a named type or fail

    Throw an error a caller can catch, or return a result object. Do not return a half-parsed record.

  3. 03

    Keep casts inside the parser

    A cast in a view is a sign the parser stopped too early.

More guides