TypeScript - Never: Understanding the Bottom Type

Hello, aspiring programmers! Today, we're going to dive into one of TypeScript's more enigmatic types: the never type. Don't worry if you're new to programming – I'll guide you through this concept step by step, just as I've done for countless students in my years of teaching. So, grab your favorite beverage, and let's embark on this exciting journey into the world of TypeScript!

TypeScript - Never

What is the never type?

The never type in TypeScript is often referred to as the "bottom type" or the "empty type." It represents a type that should never occur. Now, you might be thinking, "Why would we need a type that never happens?" Well, my curious friends, it's more useful than you might think!

When is never used?

  1. To represent impossible scenarios
  2. To handle exhaustive checks
  3. In functions that never return

Let's look at some examples to make these concepts clearer.

Example 1: Representing Impossible Scenarios

function throwError(message: string): never {
    throw new Error(message);
}

let result = throwError("Oops! Something went wrong!");
console.log(result); // This line will never be reached

In this example, the throwError function is guaranteed to throw an error and never return normally. Therefore, its return type is never.

Think of it like this: if you're baking a cake and the recipe says "bake until never," you know that cake isn't coming out of the oven!

Example 2: Exhaustive Checks

type Shape = "circle" | "square" | "triangle";

function getArea(shape: Shape): number {
    switch (shape) {
        case "circle":
            return Math.PI * Math.pow(5, 2);
        case "square":
            return 10 * 10;
        case "triangle":
            return (10 * 5) / 2;
        default:
            const _exhaustiveCheck: never = shape;
            return _exhaustiveCheck;
    }
}

Here, never helps us ensure we've covered all possible shapes. If we add a new shape to the Shape type but forget to add a case for it in getArea, TypeScript will give us an error. It's like having a helpful assistant that reminds you when you've forgotten something!

Example 3: Functions That Never Return

function infiniteLoop(): never {
    while (true) {
        console.log("This loop never ends!");
    }
}

This function will run forever (or until your computer runs out of memory). Since it never finishes executing, its return type is never. It's like telling your friend you'll stop talking "never" – they know they're in for a long conversation!

The never type vs. void

Now, you might be wondering, "How is never different from void?" Great question! Let's break it down.

void

The void type is used when a function doesn't return any value, but it does complete its execution.

function logMessage(message: string): void {
    console.log(message);
}

logMessage("Hello, TypeScript!"); // This function returns undefined

never

The never type, on the other hand, is used when a function never completes its execution or always throws an error.

function failwithError(message: string): never {
    throw new Error(message);
}

failwithError("This function never returns!");

Think of it this way: void is like going to a store and coming back empty-handed, while never is like setting out on a journey with no destination – you never come back!

Practical Uses of never

Let's look at some more practical examples where never can be useful.

Example 4: Type Guards

type Square = { kind: "square", size: number };
type Circle = { kind: "circle", radius: number };
type Shape = Square | Circle;

function assertNever(x: never): never {
    throw new Error("Unexpected object: " + x);
}

function getArea(shape: Shape) {
    switch (shape.kind) {
        case "square": return shape.size * shape.size;
        case "circle": return Math.PI * shape.radius ** 2;
        default: return assertNever(shape); // Error if shape is neither Square nor Circle
    }
}

In this example, assertNever helps us catch any case we might have missed. It's like having a safety net when you're learning to juggle types!

Example 5: Unreachable Code Detection

function neverReaches(): never {
    while (true) {
        // Some operation
    }
    console.log("This line will never be reached");  // TypeScript error
}

TypeScript is smart enough to know that the console.log statement will never be reached, and it will give you an error. It's like having a GPS that tells you when you're trying to drive to a place that doesn't exist!

Methods and Properties of never

Now, you might be wondering if never has any methods or properties. The truth is, never doesn't have any of its own methods or properties because it represents a type that should never occur. However, it's still an important part of TypeScript's type system.

Here's a table summarizing what you can (or can't) do with never:

Operation Result Explanation
Assign to never ✅ Allowed Any type can be assigned to never
Assign never to other types ❌ Not Allowed never can't be assigned to any other type
Call methods on never ❌ Not Allowed Since never should never occur, you can't call methods on it
Use never in unions ✅ Allowed but has no effect never is ignored in union types
Use never in intersections ✅ Allowed and results in never Any type intersected with never results in never

Conclusion

And there you have it, my dear students! We've journeyed through the land of never, exploring its nooks and crannies. Remember, never is like that friend who always cancels plans – they never show up, but they're still important to keep in mind!

Understanding never might seem tricky at first, but with practice, you'll find it's an invaluable tool in your TypeScript toolkit. It helps make your code more robust, catches potential errors, and even makes you think more deeply about your function's behavior.

Keep coding, keep learning, and never say never to trying new things in TypeScript! Until next time, may your compilation errors be few and your type inferences be strong!

Credits: Image by storyset