iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

TS Type Guards

A type guard is a runtime check that TypeScript uses to narrow types inside a block. The built-ins cover most cases; user-defined guards handle the rest.

Built-in guards

CheckNarrows
typeof x === 'string'Primitives.
x instanceof DateClasses.
'foo' in objObject property existence.
Array.isArray(x)Array.
Equality on a discriminantDiscriminated unions.

User-defined type predicate

TS
function isString(x: unknown): x is string {
    return typeof x === 'string';
}

function format(x: unknown) {
    if (isString(x)) {
        return x.toUpperCase();    // x: string — narrowed!
    }
    return String(x);
}

The return type x is string tells TS "if this function returns true, narrow x to a string".

Validating shape

TS
type User = { id: number; name: string };

function isUser(v: unknown): v is User {
    return (
        typeof v === 'object' &&
        v !== null &&
        'id'   in v && typeof (v as any).id   === 'number' &&
        'name' in v && typeof (v as any).name === 'string'
    );
}

function welcome(v: unknown) {
    if (isUser(v)) {
        console.log('Hi', v.name);
    }
}

For real validation — reach for a library

Hand-rolled guards work for one or two shapes. For larger schemas use:

  • Zod — TS-first, parse and infer types.
  • Valibot — smaller bundle, similar API.
  • ArkType — TS-syntax-like schema definitions.
TS
import { z } from 'zod';

const User = z.object({ id: z.number(), name: z.string() });
type User  = z.infer<typeof User>;

const u = User.parse(json);     // throws if shape is wrong; u: User after

assertion functions

TS
function assertString(x: unknown): asserts x is string {
    if (typeof x !== 'string') throw new TypeError('not a string');
}

let x: unknown = 'hi';
assertString(x);
x.toUpperCase();          // ✓ narrowed
Tip: For data crossing a runtime boundary (JSON, env vars, query strings), prefer a real validator over a hand-rolled guard. One source of truth — type + runtime check — beats two that can drift apart.

Example

Example
function isString(x: unknown): x is string {
  return typeof x === 'string';
}
function format(x: unknown) {
  if (isString(x)) return x.toUpperCase();   // narrowed
  return String(x);
}
console.log(format('hi'), format(42));
Try it Yourself »

Exercise

Return type of a user-defined guard for string.

function isString(x: unknown): x string { ... }

Test yourself

Q1. User-defined guard return type is written…
Q2. Assertion functions return type is…
Q3. For real validation prefer…

Discussion

Loading…