Having Fun with TypeScript: any, unknown and never

Dagang Wei
3 min readMar 27, 2024

--

This blog post is part of the series Having Fun with TypeScript.

Introduction

TypeScript is a powerful addition to JavaScript, layering a robust type system on top of the language’s foundation. By understanding its different types and how to use them, you write much safer and more predictable code. In this post, we’ll delve into three fundamental types that help you manage uncertainty and impossible scenarios: ‘any’, ‘unknown’, and ‘never’.

any: Flexibility at the Cost of Safety

‘any’ is essentially an escape hatch from TypeScript’s type checking. It allows a variable to hold a value of absolutely any type — number, string, object, you name it.

When to Use:

  • Migrating a JavaScript project to TypeScript gradually
  • Working with 3rd party libraries that don’t have type definitions
  • Prototyping where types are not yet fully defined

The Dangers of ‘any’: Using ‘any’ disables much of TypeScript’s benefits because it can lead to runtime errors that compile-time checks might have caught. You lose type safety and the compiler can’t help you much. Think of ‘any’ as a last resort.

Example:

let something: any = 42; 
something = "Hello"; // No problem with TypeScript
something.someMethod(); // This could crash at runtime if 'something' isn't an object with a 'someMethod'

unknown: The Type-Safe Alternative

‘unknown’ is a much safer counterpart to ‘any’. It represents a variable whose type we don’t yet know.

When to Use:

  • Dealing with untrusted data from user input or 3rd party APIs
  • When type information is missing but you want to preserve type safety.

Enforcing Checks: Unlike ‘any’, you cannot directly perform operations on ‘unknown’ variables. TypeScript forces you to do type narrowing before using them.

Example:

function processData(data: unknown) {
if (typeof data === 'string') {
// TypeScript now understands data is a string here
console.log(data.toUpperCase());
} else if (typeof data === 'number') {
// TypeScript considers data a number in this block
console.log(data * 2)
}
}

never: Representing the Impossible

The ‘never’ type signifies a value that will never occur

When to Use:

  • Functions that never return (e.g., throw an exception, loop infinitely)
  • Exhaustive checking in switch/case statements to ensure all cases are handled

Example 1:

function keepProcessing(): never { 
while (true) {
console.log('I always does something and never ends.')
}
}

Example 2:

type Color = "red" | "blue" | "green";

function assertUnreachable(x: never): never {
throw new Error("Didn't expect to get here");
}

function f(c: Color): number {
switch(c) {
case "red":
return 1;
case "blue":
return 2;
case "green":
return 3;
default:
assertUnreachable(c);
}
}

console.log(f("blue"));

Key Takeaways

  • Strive to avoid ‘any’ whenever possible. It leads to less maintainable code over time.
  • ‘unknown’ provides type safety when dealing with unpredictable data sources.
  • ‘never’ helps signal impossible states and enforces exhaustive checks.

By understanding and applying ‘any’, ‘unknown’, and ‘never’ strategically, you’ll write far more robust and reliable TypeScript code.

References

--

--