2

If I have a function that under normal circumstances returns a value, but can throw an exception what should the prototype look like? Should it include | never (e.g., a union of its normal return type and never)?

For example, is there reason to do the following? If nothing else it seems like it might point out possible exceptions to future coders.

function fn( n: number ) : number | never {
    if ( n < 0 ) throw ("badness");
    return n;
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
Scott VanKirk
  • 445
  • 2
  • 5
  • 8
  • 3
    Possible duplicate of [How to declare a function that throws an error in Typescript](https://stackoverflow.com/questions/49434751/how-to-declare-a-function-that-throws-an-error-in-typescript) – JJJ Oct 22 '18 at 16:17
  • 1
    This is an interesting question, but it might be a bit too dependent on opinion to be answerable. (the possible duplicate question only talks about the how, not about the 'should') – Simon Groenewolt Oct 22 '18 at 16:25

1 Answers1

0

It doesn't hurt, but there's not much point to doing that. The never acts as a bottom type with no possible values, and is aggressively removed from unions by the compiler:

Because never is a subtype of every type, it is always omitted from union types and it is ignored in function return type inference as long as there are other types being returned.

That means if you inspect your function with something like IntelliSense, it will show up as:

function fn(n: number): number

so future coders are unlikely to even notice the difference. And in practice, there is no difference, because it's very hard to guarantee that an exception cannot be thrown inside some function.


A safer but more annoying approach is to encode possible failure in your return type without relying on exceptions, such as:

function fn( n: number ) : number | undefined {
    if ( n < 0 ) return undefined;
    return n;
}

or even more verbose but very explicit about intent:

type Success<T> = { success: true, value: T };
type Error<E> = { success: false, error: E };
function fn(n: number): Success<number> | Error<string> {
  if (n < 0) return { success: false, error: "badness" };
  return { success: true, value: n };
}

It really depends on your use case.


Probably I'd just leave your original as-is and use JSDoc comments to hint about possible exceptions.

/** Returns n if n is non-negative, throws an exception otherwise */
function fn(n: number): number | never {
  if (n < 0) throw ("badness");
  return n;
}

If you are using an editor with IntelliSense, the hints will show up:

IntelliSense view

Hope that helps. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360