The never type



  • Been working with Typescript, and came across this bizarre primitive.

    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.


  • sockdevs

    @The_Quiet_One How is that functionally different to returning void?



  • @RaceProUK

    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?


  • Winner of the 2016 Presidential Election

    @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?

    Filed Under: :wtf:



  • 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.


  • Winner of the 2016 Presidential Election

    @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 type Nothing.



  • @hungrier said in The never type:

    @RaceProUK

    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.


  • Winner of the 2016 Presidential Election

    @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 complicated

    Since 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 complicated

    Since 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. :wtf:



  • @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'.


  • Winner of the 2016 Presidential Election

    @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 :doing_it_wrong:.


  • mod

    @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.


  • sockdevs

    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);
    }
    

  • Discourse touched me in a no-no place

    @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. The never type has no values possible at all, and so indicates something illegal (like returning normally from a function that never returns normally).


  • Winner of the 2016 Presidential Election

    @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. The never 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 called nothing 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.


  • Discourse touched me in a no-no place

    @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 and undefined as distinct values of different single-value types. In this area, “unique” is :headdesk:


  • sockdevs

    @dkf I'm pretty sure JavaScript doesn't have a nothing, only a null. And undefined, but that's a different thing anyway.


  • Winner of the 2016 Presidential Election

    @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.


  • Discourse touched me in a no-no place

    @RaceProUK Derp! I meant undefined. Updated…


  • sockdevs

    @dkf I think it makes sense to have null and undefined, as they have different semantics: null means there's no value, and undefined I'll leave up to the reader to work out :slight_smile:


  • Winner of the 2016 Presidential Election

    This post is deleted!


  • @RaceProUK having undefined value is a terrible idea, compared to the alternative solution of throwing an UndefinedValue 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.


  • Discourse touched me in a no-no place

    @NeighborhoodButcher said in The never type:

    And nothing everything is better than JS.

    FTFY



  • @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 :stuck_out_tongue: )


  • Impossible Mission - B

    @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 and undefined as distinct values of different single-value types. In this area, “unique” is :headdesk:

    In just about every area in the programming languages field, "unique" is :headdesk:. (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.


Log in to reply
 

Looks like your connection to What the Daily WTF? was lost, please wait while we try to reconnect.