Skip to main content

Type Assertions and Type Guards

Sometimes TypeScript needs help understanding the specific type of a value. Type assertions and type guards help us tell TypeScript exactly what we're working with.

Type Assertions

Type assertions are like telling TypeScript "trust me, I know what this is":

// Using 'as'
const myCanvas = document.getElementById('main_canvas') as HTMLCanvasElement;

// Using angle-bracket syntax (not used in TSX files)
const myCanvas2 = <HTMLCanvasElement>document.getElementById('main_canvas');

Type Guards

Type guards are runtime checks that guarantee the type in a certain scope:

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

function processValue(value: unknown) {
if (isString(value)) {
// TypeScript knows value is a string here
console.log(value.toUpperCase());
}
}

Common Type Guards

typeof

function printValue(value: string | number) {
if (typeof value === "string") {
// TypeScript knows value is a string
console.log(value.toUpperCase());
} else {
// TypeScript knows value is a number
console.log(value.toFixed(2));
}
}

instanceof

class Car {
drive() { /* ... */ }
}

class Bicycle {
pedal() { /* ... */ }
}

function operate(vehicle: Car | Bicycle) {
if (vehicle instanceof Car) {
vehicle.drive();
} else {
vehicle.pedal();
}
}

Discriminated Unions

A common pattern using type guards:

type Circle = {
kind: "circle";
radius: number;
};

type Square = {
kind: "square";
sideLength: number;
};

type Shape = Circle | Square;

function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
}
}

Best Practices

  1. Avoid type assertions when possible
  2. Use type guards for better type safety
  3. Make discriminated unions explicit
  4. Keep type guards simple and focused

Practice Exercise

Create a function that processes different types of user input (string, number, or boolean) and returns appropriate responses using type guards.