TypeScript: Why is never assignable to every type?

I understand that the issue tracker is not for questions, which is why I’ve already asked this on StackOverflow:

https://stackoverflow.com/questions/53540282/why-is-never-assignable-to-every-type

But as I haven’t gotten an answer to the main question in almost two weeks, I thought I’d try here as well:


The TypeScript documentation says that

The never type is a subtype of, and assignable to, every type

but doesn’t mention why.

Intuitively, I would expect code like this to fail:

const useString = (str: string) => console.log('This is definitely a string:', str)
const useNever = (not_a_string: never) => useString(not_a_string)

but there are no errors, because any never value is considered a valid string.

Is this intentional? If yes, then why? 😃

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 8
  • Comments: 16 (5 by maintainers)

Most upvoted comments

The never type is meant for functions that never return.

A function that throws an exception is a function that never returns.

function throws () : never {
    throw new Error("I am an error");
}

A function that gets stuck in an infinite loop also has a return type of never.


This function returns number|never

function russianRoulette () : never|number {
    const r = Math.random();
    if (r < 1/6) {
        throw new Error("You are dead");
    } else {
        return r;
    }
}

However, if russianRoulette() does return a value, it’ll return a value of type number.

//Inferred type of `f` is `number`
const f = russianRoulette();

This also works,

const f : number = russianRoulette();

So, we can see that a return value of type never|number is assignable to a variable of type number.


An intuitive way to think about assignability is to think about sets and subsets.

The set of {1,2,3} is a subset of the set of numbers.

Therefore, the following is allowed,

declare const _123 : 1|2|3;
const n : number = _123; //OK

The set of {1,2,3} is not a subset of the set {2,3,4}.

Therefore, the following is not allowed,

declare const _123 : 1|2|3;
const _234 : 2|3|4 = _123; //NOT OK

So… What is the set of nevers?

What values are of type never? Well… No values are of type never. The set of never is an empty set. It has no values. No elements.

And the empty set is a subset of… Every other set. The set of numbers. The set of strings. Etc.


This is also why never|number is the same type as number. And also why never|<any-other-type> is the same as <any-other-type>.

Since, given a set S, the union of S and the empty set is just S


const nvr1: never = 3; //NOT OK
const nvr2: never = "hello, world"; //NOT OK

Being the empty set, no values are assignable to a variable of type never.

These are very good answers. I also finally got a proper answer on SO (probably thanks to this issue) that mentions the logical principle of explosion.

And in theory, this all makes sense now.

…but… 😃

In practice, things like this can happen: lib.dom.d.ts contains declare const name: never;

Which means that any TypeScript code targeting a browser has a global “impossible variable” with a very common name. Namely, name.

And due to the principle of explosion, if an impossible variable exists, everything false might as well be true 😃

For a more concrete example, let’s say a developer makes a mistake like this:

const useStringArray = (strArr: string[]) => strArr.join(',')
const names = ['Foo', 'Bar']
useStringArray(name) // Meant to use names

This is valid TypeScript, but throws an error at runtime.

Or perhaps you had a local name variable, then removed it and expected TypeScript to give you errors about any remaining references to it. But all those practically broken references are still theoretically valid, because never is everything 😃

This has resulted in actual bugs for me on several occasions. So far, my workaround has been “Never use name as a name”. But I’m wondering if there are better solutions for this.

Maybe the global name could be declared differently? As a string? Or unknown?

Maybe disallow using variables of type never where variables of some other type are expected? In theory, this would be wrong. But in practice, maybe the benefits would outweigh the costs?

Alternatively, I guess a TSLint rule could be created that checks for this.

I think we’ve lost sight of the original goal of this issue - the issue was specifically that never is assignable to everything so the global name being declared as never doesn’t actually prevent all errors. It prevents it being assigned to, but doesn’t catch cases where it’s used in an expression.

The problem here is that there’s no type that means “you can’t assign to OR from it”—even if you make an impossible intersection that doesn’t collapse to never, it will nonetheless act like never. Under structural typing, any type which is effectively an empty set is unambiguously a subtype of everything.

If you want to collapse “impossible” intersections to never, union them with never:

Original:

type Never1 = number & string & boolean
type Never2 = number & boolean
type Never3 = string & boolean
type NotNever1 = string & number
type NotNever2 = number & bigint
type NotNever3 = string & void
type Null1 = string & null
type Null2 = number & null
type Undefined1 = number & undefined
type Undefined2 = boolean & undefined

All never:

type Never1 = number & string & boolean | never
type Never2 = number & boolean | never
type Never3 = string & boolean | never
type NotNever1 = string & number | never
type NotNever2 = number & bigint | never
type NotNever3 = string & void | never
type Null1 = string & null | never
type Null2 = number & null | never
type Undefined1 = number & undefined | never
type Undefined2 = boolean & undefined | never

This was decided as a compromise between the people who used & "brand" for “branding” their types (fake nominalism) and the people who wanted these to collapse to never for convenience.

@mjomble The explanation there is because boolean is a union type true | false, and empty intersections only get simplified if they appear within a union. For example:

number & string & boolean ==>
number & string & (true | false) ==>
(number & string & true) | (number & string & false) ==>
never | never ==>
never

The point is moot though because those intersection types are assignable to lots of things from the rules for intersections alone (independently of whether they denote never), so they don’t function like this none type.

Maybe the global name could be declared differently? As a string? Or unknown?

I now looked into this a bit and found some older issues.

Most of them are pointing to https://github.com/Microsoft/TypeScript/issues/15424#issuecomment-301207034

by making it never at least you will get an error and you will go back and check the source of the declaration.

However, as has been pointed out in some of the other issues, you do not actually get an error from using a never.