I have now searched the internet for an explanation of WHY the never
type was introduced. Both the TypeScript handbook and the TypeScript Deep Dive exaplains what it is and a few other articles. None of them explains, though, WHY you would use the never
type and what it solves.
Most of the examples show code, where the compiler already infers never
, so why do I have to assign the type? What does it solve. Is it “just” to signal the intellisense what I can do afterwards (i.e. Nothing - which might be useful enough). And what do the compiler use the information for?
As always, explanations of WHAT and HOW might help people do stuff, but only WHY makes them understand.
The reason the error "Argument of type is not assignable to parameter of type 'never'" occurs is because when we declare an empty object, TypeScript infers its type to be never[] - an array that will never contain any elements.
Just as boolean is either true and false , which means there are and only are 2 ways to construct a value of type boolean , in TypeScript there are exactly 0 ways to construct a value of type never . But you may ask, "how is never any useful then?" An expression f() will get you nowhere.
In Typescript, any value can be assigned to unknown, but without a type assertion, unknown can't be assigned to anything but itself and any. Similarly, no operations on an unknown are allowed without first asserting or restricting it down to a more precise type.
The error "Property does not exist on type 'never'" occurs when we try to access a property on a value of type never or when TypeScript gets confused when analyzing our code. To solve the error, use square brackets to access the property, e.g. employee['salary'] .
The never
type is TypeScript's representation of the bottom type from type theory. So if you are looking for motivation for and uses of never
in TypeScript, you might want to look into the bottom type in computer science in general.
The canonical answer to "why was never
introduced to TypeScript" is probably best found by analyzing the commentary on the pull request that introduced it and the two issues that it addresses: a request for a bottom type, and request to better handle unreachable function returns.
The main use for developers is to have a type for functions (or sections of functions) that don't return any value at all, not even undefined
. Compare these functions:
function u(): undefined {
return;
}
const uRet = u();
console.log(typeof uRet); // undefined
function v(): void { }
const vRet = v();
console.log(typeof vRet); // undefined
which return undefined
values, with these functions:
function throws(): never {
throw new Error();
}
const tRet = throws();
console.log(typeof tRet); // does not run
function loops(): never {
while (true) { }
}
const lRet = loops();
console.log(typeof lRet); // does not run
which don't return values at all, due to an exception or an infinite loop. The never
type allows both developers and the compiler to reason about sections of code which can never run.
There are other uses for never
but I am not going to try to enumerate them, since you already are aware of code examples. It is illuminating to examine the TypeScript standard library to find the places where never
shows up.
Finally, when you ask
Most of the examples show code, where the compiler already infers
never
, so why do I have to assign the type?
note that even in cases where the compiler infers never
for you, it is useful for it to exist in the language, in the same way that string
is useful even when it is inferred by the compiler. This seems like a different question about when you might want to explicitly annotate types instead of letting the compiler infer them, and vice versa. But this post is already long and this is probably answered elsewhere.
Hope that helps; good luck!
Edit: There are plenty of reasons a programmer would use never
, although many programmers might never need to use it. Let me enumerate some reasons I can think of:
If you are writing typings for a JavaScript library where a function throws an exception instead of returning, you can't really do that without never
.
Generic type parameters default to {}
when they can't be inferred by the compiler. It sort of "fails open", since {}
is compatible with (almost) all values. There are cases when you'd prefer it to "fail closed" and have it be compatible with no values. You can use never
as the default parameter.
The union of any type T
with never
is just T
, and the intersection of T
with never
is never
. These rules (among others) let a developer build fairly sophisticated type functions that would be harder or impossible without never
. For example, here's Diff<T,U>
:
type Diff<T extends string, U extends string> =
({ [P in T]: P } & { [P in U]: never } & { [x: string]: never })[T];
It is a type function which takes a union of string literals T
and a union of string literals U
, and returns a union of all the values in T
which are not present in U
.
You can use it to, among other things, remove a property from a type:
type Foo = { a: string, b: number, c: boolean };
type FooWithoutB = Pick<Foo, Diff<keyof Foo, 'b'>>;
// equivalent to { a: string, c: boolean }
It's kind of like the number zero in mathematics. You don't need it; many cultures got along okay without the concept of zero. But it's incredibly useful and allows you to easily express ideas that would otherwise be cumbersome or impossible.
Hopefully that is a little more compelling? Anyway, good luck again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With