TypeScript Type Guards
Narrow types with typeof, instanceof, in, and custom predicates
Overview
Type guards narrow a union to a specific type inside a conditional block. Built-in guards use typeof, instanceof, and in. User-defined type predicates (value is T) encapsulate reusable checks.
Syntax / Usage
function process(value: string | number) {
if (typeof value === 'string') {
return value.trim()
}
return value.toFixed(2)
}
class Dog { bark() {} }
class Cat { meow() {} }
function speak(pet: Dog | Cat) {
if (pet instanceof Dog) pet.bark()
else pet.meow()
}
interface Fish { swim(): void }
interface Bird { fly(): void }
function move(animal: Fish | Bird) {
if ('swim' in animal) animal.swim()
else animal.fly()
}
// User-defined type guard
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value
)
}
Examples
Filter with type predicate:
const items: (string | null)[] = ['a', null, 'b']
const strings = items.filter((x): x is string => x !== null)
// string[]
Assert non-null after check:
function getUser(id: string | undefined): User {
if (!id) throw new Error('Missing id')
// id is string here
return fetchUser(id)
}
Common Mistakes
- Type predicates that lie—runtime check must actually guarantee the type
- Using
ascasts instead of real guards when data is external typeof null === 'object'historical quirk—checkvalue !== null- Narrowing lost when values are captured in closures—re-check inside callbacks
See Also
unions basic-types interfaces utility-types