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
| Check | Narrows |
|---|---|
typeof x === 'string' | Primitives. |
x instanceof Date | Classes. |
'foo' in obj | Object property existence. |
Array.isArray(x) | Array. |
| Equality on a discriminant | Discriminated 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 { ... }
Two letters.
Discussion
Loading…