Font Awesome Free 5.13.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
Skip to main content

Type guards (TypeScript)

An overview of how to use TypeScript type guards, including creating your own custom ones

Basic idea

  • Test, at runtime, whether a certain value is of a certain type
  • TypeScript compiler can use this information to make better assumptions

Built-in type guards

typeof type guards

function test(input: string | string[]) {
input.split(""); // compiler error
input.filter(() => true); // compiler error

if (typeof input === "string") {
// input has type string here
return input.split("");
} else {
// input has type string[] here
return input.filter(() => true);
}
}

instanceof type guards

class ClassA {
methodA() {
return true;
}
}

class ClassB {
methodB() {
return true;
}
}

function test(instance: ClassA | ClassB) {
instance.methodA(); // compiler error
instance.methodB(); // compiler error

if (instance instanceof ClassA) {
// instance has type ClassA here
instance.methodA();
} else {
// instance has type ClassB here
instance.methodB();
}
}

Type guards based on common property

Can be used to implement Discriminating Unions (see Discriminating Unions ):

  • Types with common property indicating type
  • A type alias that is the union of these types
  • Type guards on the common property

Example:

interface Square {
kind: "square";
size: number;
}

interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}

interface Circle {
kind: "circle";
radius: number;
}

type Shape = Square | Rectangle | Circle;

function area(shape: Shape) {
switch (shape.kind) {
case "square":
// shape has type Square here
return shape.size * shape.size;
case "rectangle":
// shape has type Rectangle here
return shape.height * shape.width;
case "circle":
// shape has type Circle here
return Math.PI * shape.radius ** 2;
}
}

TypeScript recognizes that the common property kind determines the type here

Also works with regular if-statements:

function test(shape: Shape) {
if (shape.kind === "rectangle") {
// shape has type Rectangle here
console.log(shape.height);
}
}

Type guards based on available properties

Example code (uses the interfaces from the above example):

function test(shape: Shape) {
if ("radius" in shape) {
// shape has type Circle here
}
}

User-defined type guards

You can also define your own type guards that perform the type check at runtime by looking at certain properties of the object you receive

interface Message {
messageType: string;
}

interface UserMessage extends Message {
messageType: "user";
userId: number;
}

interface OrderMessage extends Message {
messageType: "order";
orderId: number;
}

function isMessage(arg: any): arg is Message {
return typeof arg.messageType === "string";
}

function isUserMessage(arg: Message): arg is UserMessage {
return arg.messageType === "user";
}

function isOrderMessage(arg: Message): arg is OrderMessage {
return arg.messageType === "order";
}

function test(input: object) {
console.log(input.messageType); // compiler error

if (isMessage(input)) {
// input has type Message here

console.log(input.messageType);
console.log(input.userId); // compiler error
console.log(input.orderId); // compiler error

if (isUserMessage(input)) {
// input has type UserMessage here
console.log(input.userId);
} else if (isOrderMessage(input)) {
// input has type OrderMessage here
console.log(input.orderId);
}
}
}

Resources