The never type
-
Been working with Typescript, and came across this bizarre primitive.
https://basarat.gitbooks.io/typescript/docs/types/never.html
The never type is used in TypeScript to denote this bottom type. Cases when it occurs naturally:
A function never returns (e.g. if the function body has while(true){})
A function always throws (e.g. in function foo(){throw new Error('Not Implemented')} the return type of foo is never)Words fail me.
-
@The_Quiet_One How is that functionally different to returning
void
?
-
There's no difference in practice anyway since it all turns into Javascript. But it could flag "unreachable code" if you're doing something after invoking a
never
typed function.
-
@The_Quiet_One So it's a bottom type? What's the problem?
-
@The_Quiet_One said in The never type:
A function never returns (e.g. if the function body has while(true){})
@RaceProUK said in The never type:
How is that functionally different to returning void?
-
Yeah, it's logical and useful.
In many languages, the equivalent functionality is implemented as an attribute (E.g. NoReturn) rather than a type, but the intent is the same and having it as a type is quite elegant.
-
@The_Quiet_One
Maybe it's easier to understand if you think in terms of intersection types. It's the intersection of all types, which is impossible and therefore cannot be instantiated. Ceylon calls the same typeNothing
.
-
@hungrier said in The never type:
There's no difference in practice anyway since it all turns into Javascript. But it could flag "unreachable code" if you're doing something after invoking a
never
typed function.That's probably the best explanation one can give. However, the two examples they give as to how a function would return "never" sounds more like itself an error condition. I mean, yes, one can throw "Not Implemented" but that's usually a case where you would be either:
a.) Defining an abstract function that needs to be overridden. In which case the return types should be the same.
b.) You are defining a stub that needs to be implemented in the future, in which case the return type should be whatever you plan on having it.@Captain said in The never type:
@The_Quiet_One So it's a bottom type? What's the problem?
Javascript (and by extension Typescript) already has null, undefined, void, and possibly some other obscure bottom types.
-
@The_Quiet_One said in The never type:
null, undefined, void
None of those are equivalent or even similar to the bottom type from the POV of the type system.
-
@The_Quiet_One They give a good example in the article itself:
function fail(msg) { throw new Exception(msg); } // actual function can be more complicatedSince it returns never, the compiler allows you to call it at the end of another function without returning anything afterwards.
Similarly, if you call it in another function and then continue to do other stuff, the compiler will alert you to the bug - you have unreachable code.
-
@CreatedToDislikeThis said in The never type:
@The_Quiet_One They give a good example in the article itself:
function fail(msg) { throw new Exception(msg); } // actual function can be more complicatedSince it returns never, the compiler allows you to call it at the end of another function without returning anything afterwards.
Similarly, if you call it in another function and then continue to do other stuff, the compiler will alert you to the bug - you have unreachable code.Hmm... okay, now it's making more sense to me.
Now, the reason I stumbled upon it is still confusing to me. I was writing a function that referenced a "name" variable I discovered was in scope when it shouldn't have been, and it had this "never" type. I right-clicked to go to definition and "const name: never;" in the default Typescript lib.d.ts.
-
@The_Quiet_One Weird.
The 'name' global is declared there (together with many friends like 'closed') because these are attributes of a window object, and in Javascript the global object is a window object (yes, that's a TRWTF if there ever was one).
The 'name' global should be a string, though, just like it is on the Window 'interface' - I'm not at all sure why it's declared as 'never'.
-
@CreatedToDislikeThis said in The never type:
I'm not at all sure why it's declared as 'never'.
Unless this is an error in the IDE, that'd mean they want to make its usage in expressions a compiler error. Weird.
-
@CreatedToDislikeThis said in The never type:
In many languages, the equivalent functionality is implemented as an attribute (E.g. NoReturn) rather than a type, but the intent is the same and having it as a type is quite elegant.
In Javascript, functions that never return are .
-
@RaceProUK said in The never type:
@The_Quiet_One How is that functionally different to returning
void
?The quoted site explicitly explains:
A function that returns nothing returns a Unit void. However a function that never returns (or always throws) returns never. void is something that can be assigned (without strictNullChecking) but never can never be assigned to anything other than never.
-
void is something that can be assigned
That just raises more questions…
-
See also:
[[noreturn]]
:Indicates that the function does not return.
This attribute applies to function declarations only. The behavior is undefined if the function with this attribute actually returns.
The following standard functions have this attribute:std::_Exit
,std::abort
,std::exit
,std::quick_exit
,std::unexpected
,std::terminate
,std::rethrow_exception
,std::throw_with_nested
,std::nested_exception::rethrow_nested
-
In Rust, there's the
!
type. It can never be instantiated, but it can be implicitly converted to any other type - which means that functions that never return can be used in any context, even if return value is then "used" for something by caller (it won't be actually used, since callee never returns - but type checker is happy).As for functions that never return - there are two kinds that make sense: infinite loops and process/thread termination.
Edit: one more thing - if you declare a function returning
!
, the type checker actually verifies if your function never returns - because any possible code path that could end with return would try to return a value that's not of!
type.
-
The underlying problem that
never
was created to solve was flow-based type reductions:type AnythingCool = 'walruses' | 'seahorses' | 'yetis'; function talkCool(about: AnythingCool) { switch(about) { case 'walruses': case 'seahorses': case 'yetis': return `${about} are cool`; } // Typescript will flag this as an error because there's no case where `about` // will be anything you expect it to be // null or undefined would both be even *more* confusing // since it can *clearly* not be either of those things // (And that would make it impossible to remove nullity or "missingness" from another union type) const aBitOfIt = about.substring(1, 3); }
-
@RaceProUK said in The never type:
How is that functionally different to returning
void
?At the type algebra level, the
void
type effectively has only a single value possible, which can always be optimised out and never talked about since it is utterly statically immutable. Thenever
type has no values possible at all, and so indicates something illegal (like returning normally from a function that never returns normally).
-
@dkf said in The never type:
At the type algebra level, the
void
type effectively has only a single value possible, which can always be optimised out and never talked about since it is utterly statically immutable. Thenever
type has no values possible at all, and so indicates something illegal (like returning normally from a function that never returns normally).Fun fact: In Ceylon, there's a single
Nothing
value callednothing
as well:Using it anywhere the compiler expects an expressions is a compile-time error. No, I have no clue what they're using it for.
-
@dkf said in The never type:
At the type algebra level, the
void
type effectively has only a single value possible, which can always be optimised out and never talked about since it is utterly statically immutable.Also, Javascript is pretty much unique in having
null
andundefined
as distinct values of different single-value types. In this area, “unique” is
-
@dkf I'm pretty sure JavaScript doesn't have a
nothing
, only anull
. Andundefined
, but that's a different thing anyway.
-
@dkf said in The never type:
Also,
JavascriptCeylon is pretty much unique in having null and nothing as distinct values of different single-value types.FTFY. JavaScript doesn't have
nothing
.
-
@RaceProUK Derp! I meant
undefined
. Updated…
-
@dkf I think it makes sense to have
null
andundefined
, as they have different semantics:null
means there's no value, andundefined
I'll leave up to the reader to work out
-
This post is deleted!
-
@RaceProUK having
undefined
value is a terrible idea, compared to the alternative solution of throwing anUndefinedValue
exception.
-
I didn't read through all the comments so I may be repeating someone, but the never type is actually a good idea. It's nothing more than a compile time check if you want to give the result of something which never should return one. Granted, these are niche cases, but it's better than nothing. And nothing is better than JS.
-
-
@dkf damn English language. Should have put nothing in "". We should all speak Korean.
-
@NeighborhoodButcher said in The never type:
@dkf damn English language. Should have put nothing in "". We should all speak Korean.
Samsung is trying...
-
@RaceProUK said in The never type:
How is that functionally different to returning
voidanything?The latter returns, the former doesn't.
(well, you asked )
-
@dkf said in The never type:
@dkf said in The never type:
At the type algebra level, the
void
type effectively has only a single value possible, which can always be optimised out and never talked about since it is utterly statically immutable.Also, Javascript is pretty much unique in having
null
andundefined
as distinct values of different single-value types. In this area, “unique” isIn just about every area in the programming languages field, "unique" is . (Features that are actually good tend to get borrowed and reused in other languages!)
-
@RaceProUK said in The never type:
@The_Quiet_One How is that functionally different to returning
void
?void has one zero-length value.
never has zero values of any length.
If your function says it returns never, it cannot return.
-
@ben_lubar said in The never type:
If your function says it returns never, it cannot return.
But it can throw an error/exception. Those formally become an auxiliary type, so you can know that the function can't return normally, but has a given set of errors that it might throw (where opinions differ vastly on how exactly that set of errors should be known). This is useful because it also allows you to distinguish the case where the compiler can prove that a function does not throw, which in turn enables a lot more optimisations.
-
Came for this, leaving disappointed
-
@dkf said in The never type:
@ben_lubar said in The never type:
If your function says it returns never, it cannot return.
But it can throw an error/exception. Those formally become an auxiliary type, so you can know that the function can't return normally, but has a given set of errors that it might throw (where opinions differ vastly on how exactly that set of errors should be known). This is useful because it also allows you to distinguish the case where the compiler can prove that a function does not throw, which in turn enables a lot more optimisations.
It can send control to some part of the program, it just can't jump to the return address. Sending control elsewhere isn't the same as returning.