Type Predicates
Published on
The Problem
Let's say you have an array of mixed types, and you want to filter it down to just the strings:
const mixed = [1, 'hello', true, 'world', 42]; const strings = mixed.filter(item => typeof item === 'string'); // Type: (string | number | boolean)[]
If you check the type of strings
, you'll see that it's (string | number | boolean)[]
. TypeScript isn't smart enough
to know that the filter function has narrowed the type of the array.
The Solution
You can define a function that returns a type predicate, which tells the TypeScript compiler that if the function
returns true
, the value is of a certain type:
function isString(value: unknown): value is string { return typeof value === 'string'; } const mixed = [1, 'hello', true, 'world', 42]; const strings = mixed.filter(isString); // string[]
Now, the type of strings
is string[]
, which is exactly what we want.
Usage
Filtering Arrays
function isNumber(value: unknown): value is number { return typeof value === 'number'; } const mixed = [1, 'hello', true, 'world', 42]; const numbers = mixed.filter(isNumber); // number[]
Checking Object Properties
interface User { name: string; email: string; } function isUser(obj: unknown): obj is User { return ( typeof obj === 'object' && obj !== null && 'name' in obj && 'email' in obj && typeof obj.name === 'string' && typeof obj.email === 'string' ); } const data: unknown = { name: 'John', email: 'john@example.com' }; if (isUser(data)) { console.log(data.name); // TypeScript knows data is User console.log(data.email); }
Narrowing Union Types
type Cat = { type: 'cat'; meow: () => void }; type Dog = { type: 'dog'; bark: () => void }; type Animal = Cat | Dog; function isCat(animal: Animal): animal is Cat { return animal.type === 'cat'; } function handleAnimal(animal: Animal) { if (isCat(animal)) { animal.meow(); // TypeScript knows animal is Cat } else { animal.bark(); // TypeScript knows animal is Dog } }
Filtering Null/Undefined Values
function isDefined<T>(value: T | undefined | null): value is T { return value !== undefined && value !== null; } const values = [1, undefined, 2, null, 3]; const definedValues = values.filter(isDefined); // number[]