Closed
Description
TS 2.4.1 with noImplicitReturns
enabled
class A {}
class B {}
// error: Not all code paths return a value.
const fn = (x: A | B) => {
if (x instanceof A) {
x // A
return 1
} else if (x instanceof B) {
x // B
return 2
} else {
x // never
}
}
I would not expect this to error because the if statement is exhaustive, therefore all code paths do return a value.
Metadata
Metadata
Assignees
Type
Projects
Milestone
Relationships
Development
No branches or pull requests
Activity
j-oliveras commentedon Jul 22, 2017
Is not exhaustive you can call fn function as
fn({})
orfn({ x: 1 })
. These calls are valid and are not A nor B.So, I think, that works as intended.
OliverJAsh commentedon Jul 22, 2017
@j-oliveras I think what you're saying makes sense, but I don't see how that applies to this example:
If using a
switch
statement instead of theif
, the error goes. I would like to understand why we get this error when usingif
statements.jcalz commentedon Jul 23, 2017
@j-oliveras It is exhaustive as far as the type checker is concerned, since it infers a
never
type forx
in the finalelse
clause. Of course, as you note, you can fool it, due to an inconsistency between the nominal typing ofinstanceof
at runtime and the structural typing of TypeScript (see, e.g., #11664). But that's not the issue here, as @OliverJAsh's followup demonstrates.The simplest repro I can imagine is something like:
I'm guessing that the real issue is that the control flow analysis just isn't clever enough to realize that, once it has narrowed something to the
never
type, the surrounding code is unreachable and shouldn't count as a code path for the purposes ofnoImplicitReturns
. Obviously there are workarounds (rework theif
/else
clauses to be obviously exhaustive to the compiler; throw an exception in any code you know is unreachable; etc.) but the question is whether the reported issue is a design limitation or a bug. I don't think it should be intended behavior.DanielRosenwasser commentedon Jul 24, 2017
I believe the problem is that only
switch
statements are special cased here. Basically, if the last statement in a function is aswitch
statement, we do an extra check for exhaustiveness, but we decided thatif
/else
s might be too complex of a check. You'd have to check with @ahejlsberg for specifics.You can always return the result of calling
assertNever
(documented here, available on npm here.mhegazy commentedon Aug 17, 2017
Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.
OliverJAsh commentedon Oct 19, 2017
This also affects switch statements when they exist within an
if
statement. You can workaround it by wrapping the switch in an IIFE.Is there anything TS can do to help here?
jcalz commentedon Oct 19, 2017
@OliverJAsh said:
@DanielRosenwasser said:
OliverJAsh commentedon Oct 19, 2017
@jcalz Thanks, that works.
TypeScript already special cases
switch
statements, as mentioned by @DanielRosenwasser, and so I was wondering if it could extend this behaviour toswitch
statements withinif
statements?eucaos commentedon Nov 27, 2017
I have the same problem with nested switch clauses :
const reducer = (state: "A", action: "act1"): "A" => { switch (state) { case "A": switch (action) { case "act1": return state; } } };
I get function lacks ending return statement error message.