Typescript The Unknown And Never Types Complete Guide

 Last Update:2025-06-22T00:00:00     .NET School AI Teacher - SELECT ANY TEXT TO EXPLANATION.    10 mins read      Difficulty-Level: beginner

Understanding the Core Concepts of TypeScript The unknown and never Types

TypeScript: The unknown and never Types

Understanding the unknown Type

Definition: Introduced in TypeScript 3.0, the unknown type represents values whose types are not known until runtime. This is a more restrictive counterpart to any, as it enforces more type-safe operations on variables declared with unknown.

Key Characteristics:

  • Unlike variables of type any, those of type unknown cannot be assigned to variables of any other type (except unknown and any) without first performing runtime type checks or casts.
  • This characteristic adds layer of security against accidental type errors and helps catch many bugs during compile time rather than runtime.

Importance and Usage:

  • Function Parameters: Useful when you’re writing functions that can accept any kind of input, but must validate it before processing.
  • Dynamic Content Handling: Ideal for interacting with dynamic content like user-generated data, external APIs, etc.
  • Refactoring: When transitioning from any to more specific types, unknown serves as a safe intermediate state.

Example:

// Function that accepts any content and validates it's a string
function safelyHandleInput(input: unknown) {
    if (typeof input === 'string') {
        console.log(input.toUpperCase());
    } else {
        console.error('Invalid input type!');
    }
}

safelyHandleInput("hello"); // Outputs: HELLO
safelyHandleInput(42);      // Outputs: Invalid input type!

In the above example, input has type unknown. Before performing operations (like .toUpperCase()), TypeScript ensures that input has been correctly identified as a string through a conditional check.

Understanding the never Type

Definition: The never type signifies values that never occur. It's a subtype of every type except itself, meaning no value in TypeScript can have type never.

Key Characteristics:

  • Commonly used for function return types that always throw an exception or never properly finish.
  • Also useful when narrowing down types based on conditions that exhaust all possibilities.

Importance and Usage:

  • Error Handling Functions: Essential for declaring functions that intentionally throw errors and thus never return normally.
  • Unreachable Code: Helps identify unreachable code segments, improving the structural integrity and readability of your code.
  • Exhaustive Switch Statements: Ensures all possible cases are handled in a switch statement by forcing a compiler error if some case isn't addressed.

Example:

// Function that always throws an error
function throwError(message: string): never {
    throw new Error(message);
}

// Exhaustive switch example
function determineType(value: string | number | boolean): string {
    switch (typeof value) {
        case 'string':
            return 'String type';
        case 'number':
            return 'Number type';
        case 'boolean':
            return 'Boolean type';
        default:
            // Type never reached here since all possible types are already covered in cases
            const _exhaustiveCheck: never = value;
            return _exhaustiveCheck;
    }
}

console.log(determineType("Hello")); // Outputs: String type
console.log(determineType(10));      // Outputs: Number type

In the above code, throwError has a return type of never, indicating it will never complete normally due to the thrown exception. In the switch statement, default case uses never to ensure all expected types are explicitly handled.

Summary

Both the unknown and never types contribute to safer and more maintainable TypeScript code:

  • unknown: Makes dynamic and less controlled data interaction safer by imposing strict type guarantees.
  • never: Useful for handling error cases, unreachable code, and ensuring exhaustiveness of control structures like switches.

Online Code run

🔔 Note: Select your programming language to check or run code at

💻 Run Code Compiler

Step-by-Step Guide: How to Implement TypeScript The unknown and never Types

Type unknown

The unknown type is a safe alternative to any. It represents any value but requires you to perform some type checks before using it. This makes your code more robust and less error-prone.

Example 1: Basic Usage

Let's start with a simple example where we declare a variable of type unknown and try to work with it:

let userInput: unknown;

userInput = 'Hello World';
userInput = 42;
userInput = true;
userInput = { key: 'value' };

In this example, userInput can hold any type of value because it's declared as unknown. But if you try to use the variable directly, TypeScript will throw an error unless you narrow down the type.

Error Example

// Error: Cannot assign type 'unknown' to type 'string'
let message: string = userInput;

To fix this, you need to check the type of userInput:

Example 2: Type Guard

We can use a type guard to ensure that userInput is a string before assigning it to message:

if (typeof userInput === 'string') {
  let message: string = userInput; // Now TypeScript knows userInput is a string
  console.log(message);
} else {
  console.error('userInput is not a string');
}

Here’s what happens:

  • TypeScript narrows down the type of userInput inside the if block using the typeof operator.
  • Since typeof userInput returns 'string', TypeScript treats userInput as a string within that block.
  • You can now safely assign userInput to a string variable or call string-specific methods.

Example 3: Unknown vs. Any

To illustrate the difference between unknown and any, compare the following two examples:

Using any

let userInputAny: any;
userInputAny = 'Hello';

// No errors because TypeScript has no way to know userInputAny's type
console.log(userInputAny.toUpperCase()); 

Using unknown

let userInputUnknown: unknown;
userInputUnknown = 'Hello';

// Error: Property 'toUpperCase' does not exist on type 'unknown'.
console.log(userInputUnknown.toUpperCase());

if (typeof userInputUnknown === 'string') {
  // Inside this block, userInputUnknown is treated as a string
  console.log(userInputUnknown.toUpperCase());
} else {
  console.error('userInputUnknown is not a string');
}

As you can see, unknown forces you to add type checks before performing operations, while any allows you to skip these checks at the cost of type safety.

Type never

The never type represents values which are never supposed to occur. You typically use this type when a function will never return normally or when a variable will never be assigned a value.

Example 1: Functions That Never Return

A common use case for never is functions that always throw an error or contain an infinite loop:

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

function infiniteLoop(): never {
  while (true) {
    // do something
  }
}

In both cases, TypeScript knows these functions will never return a value, hence their return type is never.

Example 2: Type Checking

You might use never in type guard scenarios where you have exhausted all possible cases:

type Animal = 'dog' | 'cat' | 'bird';

function getAnimalDescription(animal: Animal): string {
  switch (animal) {
    case 'dog':
      return 'A dog is a loyal companion.';
    case 'cat':
      return 'Cats are independent and often mysterious.';
    case 'bird':
      return 'Birds can fly and sing.';
    
    default:
      // TypeScript will infer this line to be type 'never' 
      // because you should have covered all possible Animal types.
      const invalidAnimal: never = animal;
      throw new Error(`Unknown animal type: ${invalidAnimal}`);
  }
}

const descriptionDog = getAnimalDescription('dog'); // Valid
const descriptionCat = getAnimalDescription('cat'); // Valid
const descriptionBird = getAnimalDescription('bird'); // Valid

// Error: Argument of type 'fish' is not assignable to parameter of type 'Animal'.
const descriptionFish = getAnimalDescription('fish'); // Invalid

In this scenario:

  • The function getAnimalDescription takes an argument of type Animal, which is a union of literal types 'dog', 'cat', and 'bird'.
  • If you try to pass a value that is not part of the Animal union, TypeScript will throw an error.
  • The default case includes a never type guard that ensures you've exhaustively handled all possible values for Animal.

Exhaustive Type Checking

If you don't include the never type guard:

function getAnimalDescription(animal: Animal): string {
  switch (animal) {
    case 'dog':
      return 'A dog is a loyal companion.';
    case 'cat':
      return 'Cats are independent and often mysterious.';
    // Missing case for 'bird'
    default:
      // Error: Type 'Bird' is not assignable to type 'never'.
      let invalidAnimal: never = animal; 
      throw new Error(`Unknown animal type: ${animal}`);
  }
}

This code will produce an error because the switch statement doesn’t cover all possible Animal values (bird is missing).

By adding the never type guard in the default case, TypeScript ensures that all possible values are handled:

switch (animal) {
  case 'dog':
    return 'A dog is a loyal companion.';
  case 'cat':
    return 'Cats are independent and often mysterious.';
  case 'bird':
    return 'Birds can fly and sing.';
  default:
    let invalidAnimal: never = animal; // Ensures all cases are handled
    throw new Error(`Unknown animal type: ${invalidAnimal}`);
}

With this guard, if you ever expand the Animal type (e.g., add a new fish literal), TypeScript will remind you to handle the new case in the switch statement.

Example 3: Impossible State

Sometimes, a variable should never reach a certain state. For example:

function assertNever(value: never): never {
  throw new Error(`Unexpected value: ${value}`);
}

function describeShape(shape: 'circle' | 'square'): string {
  switch (shape) {
    case 'circle':
      return 'A circle is round.';
    case 'square':
      return 'A square has four equal sides.';
    default:
      // TypeScript will infer the 'default' case to be unreachable,
      // thus, assigning the type 'never' to it.
      return assertNever(shape);
  }
}

In this scenario:

  • The describeShape function takes a shape argument that can only be 'circle' or 'square'.
  • If the function reaches the default case, it's an indication of an unexpected state—this should never happen.
  • The assertNever function is designed to throw an error, but it also has a return type of never to indicate that it will never actually finish executing.

Summary

  • Unknown: A type representing any value, but requires type checks before working with the value. Helps prevent type-related errors that any would allow.
  • Never: A type for values that never occur, such as functions that never return or variables that should be impossible to reach.

You May Like This Related .NET Topic

Login to post a comment.