Computer scienceProgramming languagesTypeScriptData TypesBasic Types

Never and unknown

3 minutes read

In TypeScript, the never and unknown types serve different purposes. The never type represents values that never occur, and the unknown type represents values that are unknown at compile-time.

The never type

As we said in the beginning, the never type represents values that never occur. It indicates that a function will not return normally or that a variable cannot have any possible value. For example, a function that throws an error or enters an infinite loop has the return type of never.

Here's an example:

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

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

In the example above, the throwError function throws an error and never returns a value. The infiniteLoop function enters an infinite loop and never completes.

Why not use an Error type in the throwError function, you may ask. There is a specific type for errors in TypeScript, the Error type. In the case of the throwError function, the purpose is to throw an error and immediately terminate the execution of the function. By annotating the return type as never, we explicitly state that the function will never return a value. The specific error type thrown (such as Error or any custom error type) is irrelevant to the return type, as the function will not reach a return statement in any case.

never vs. void types

Now you've learned that never represents values that never occur and is used for functions that don't return normally, or variables that can't have any value. As for void, it represents the absence of any specific type and is used for functions that don't return a value but can have a return statement.

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

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

const result1: never = throwError('Oops!'); // Valid
const result2: void = logMessage('Hello!'); // Valid

const value1: never = undefined; // Error: Cannot assign undefined to never
const value2: void = undefined; // Valid

In the example, the throwError function has a return type of never because it throws an error and never completes correctly. The logMessage function has a return type of void because it logs a message but doesn't return a value.

When assigning values, you can assign undefined or null to a variable of type void, but you can't assign any value to a variable of type never.

The unknown type

As mentioned before, the unknown type represents values that are unknown at compile time. It is a type-safe counterpart of the any type. Variables of type unknown can hold values of any type, but you must perform type checks or type assertions before using them in a specific way. This helps prevent type-related errors.

Here's an example:

let userInput: unknown;

userInput = 5;
let numberValue: number;

// Type check before assigning to a specific type
if (typeof userInput === 'number') {
  numberValue = userInput; // OK
} else {
  console.log('Invalid input!');
}

Here, the userInput variable is assigned a value of 5 of type number. Before assigning it to the numberValue variable, we perform a type check to ensure that userInput is indeed a number.

Unlike the any type, the unknown type enforces type safety and requires explicit type checking, reducing the chance of runtime errors caused by incompatible types.

Some of the use cases of unknown type are;

  • Receiving data from dynamic sources. When receiving data from an external API, user input, or other dynamic sources, you may initially assign it to a variable of type unknown. This allows you to perform type checks and validations before using the data in a specific way, ensuring type safety.

  • Generic programming. When writing generic functions or classes that can operate on values of unknown types, you can use the unknown type as a placeholder. This allows you to enforce type safety by requiring explicit type checks or type assertions before performing operations on the values.

Conclusion

To summarize, never represents values that never occur, while unknown represents values that are unknown at compile-time and require type checks before use. In this topic, you learned about the never type and how it's different from the void type. You also learned about the unknown type and its use cases.

2 learners liked this piece of theory. 0 didn't like it. What about you?
Report a typo