Let’s review the basic concept of never.

Programming languages really should have a concept of bottom types, which are a natural type when you’re analyzing code flow. TypeScript is such a language for parsing code flow (😎), so it needs a reliable type that represents never happening.

The never type is the underlying type in TypeScript. Some examples of how it is naturally allocated:

  • A function that never returns a value (e.gwhile(true) {});
  • A function that always throws an error (e.g.function foo() { throw new Error('Not Implemented') }.fooIs the return type ofnever);

You can also use it as a type annotation:

let foo: never; // ok
Copy the code

However, the never type can only be assigned to another never:

let foo: never = 123; // Error: Number cannot be assigned to never

// ok, as a function return type of never
let bar: never = (() = > {
  throw new Error('Throw my hands in the air like I just dont care'); }) ();Copy the code

Details a

Never is a subtype of all types, which means that never can be assigned to all types, but vice versa:

let a: never = (function () {
    throw new Error('Not Implemented')
})();

let b: number = a;
Copy the code

In the example above, the let B line will never execute, but it does not trigger a type error. Does that make any sense, you might ask?

In fact, there is. Never is a subtype of all types, which means that all types are inverters of never. As you know, when it comes to function compatibility, parameters are inverters, so use… Arg: never[] can be compatible with any parameter:

type MyReturnType<T> = T extends(... args:never[]) => infer Return
    ? Return
    : never;
Copy the code

Details of the second

An advanced operation on never may produce a derivative of never, which I can’t find documentation for, but have found in practice:

type stillNever = [1. never];// expand never;
type stillNever = never & 1;
// join never;
type Never = never;
type stillNever = `${Never}`;
// Use never in template strings;
Copy the code

Always avoid returning never when using recursion in generic programming, as it may have the same side effects as the generic operators you use.

Details of the three

Never will automatically cut himself off when he meets an allied type:

type One = never | 1;
/ / 1
Copy the code

In fact, I feel that never itself can also be considered a union type, which is the reason for the following details.

Details of the four

Using never as a generic parameter creates a distribution mechanism for conditional types, generating types that are never regardless of the return value of the conditional type:

type IsNumber<T> = T extends number ? true : false;
type stillNever = IsNumber<never>; 
Copy the code

This feature only applies to conditional distribution, and using never as a generic parameter does not itself cause problems:

type OrOne<T> = T | 1;
type A = OrOne<never>; 
/ / 1
Copy the code

So when we want to determine whether a type is never, we need to prevent conditional distribution:

export type IsNever<T> = [T] extends [never]?true : false;
Copy the code

withvoidThe difference of

As soon as someone tells you that never means a function that never returns elegantly, you might immediately think of the similar void, whereas void actually means there is no type at all, and never means the type of a value that never exists.

When a function returns a null value, it returns a value of type void, but when a function never returns (or always throws an error), it returns a value of type never. Void can be assigned (when strictNullChecking is false), but never cannot be assigned to anything other than never itself.