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.g
while(true) {}
); - A function that always throws an error (e.g.
function foo() { throw new Error('Not Implemented') }
.foo
Is 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
withvoid
The 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.