Repository navigation
Don't widen ternary type in return statement. #31518
Description
Activity
I’m thinking the inconsistency here is that, in the first case, the entire ternary expression is inferred as
any | undefinedwhich collapses to justany; the compiler therefore sees a singlereturn anyand all is right with the world (even though it’s not). In the second case there are two return sites: one isreturn anywhich again is fine, and one isreturn undefinedwhich is not.- addedWorking as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
on May 22, 2019 RyanCavanaugh commented
on May 22, 2019 MemberMore actionsIt's not even about
return; the same problem occurs here:let n: number; const x: any = {} n = x.y ? x.y : undefined // no error const x: any = {} if (x.y) { n = x.y } else { n = undefined // error }
The consistent proposal would be that when a source expression of an assignment (or equivalent) is a ternary operator, we should check that both of its true/false operands are assignable to the target. But this just opens up more "consistency" problems:
// m: any let m = x.y ? x.y : undefined; n = m; // This is still OK, good grief!
There's not any principled place along the rabbit hole to stop going down further, plus "fixing" this would a substantial breaking change
Reacted by Bruce Pascoe and Titian Cernicova-DragomirYeah, I knew the
returnwasn’t the source of the issue; it was more a way to highlight that the compiler sees the ternary as a single expression of typeTrueCase | FalseCaserather than separate expressions as with theif-else.This union behavior is generally sound; it just so happens that unioning
anywith any other type loses all information about the other type.anyis kind of a “viral” type in that regard.typescript-bot commented
on May 25, 2019 ContributorMore actionsThis issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
Ryan Cavanaugh (@RyanCavanaugh), the
anytype is always going to create unsafe types but we can be pragmatic here. The ternary statement is functionally equivalent to the if-else statement with a hoisted variable. Why should one be more type-safe than the other? What if all ternary statements were treated by the typescript compiler the same as an if-else statement? We'd at least catch some obvious errors...I think the challenge is that a ternary isn't really a statement but an expression. An expression must have a single type; in this case that type happens to be
any | undefinedwhich is unsound simply by the nature ofany. Changing this, I suspect, would be very difficult: what happens when you have a ternary in the middle of a statement, e.g. as an argument to a function? What happens when there are several ternaries in the same statement, as I've been known to stack them to heavens:In order to type check the above the way you suggest, the compiler would basically have to duplicate the statement and individually type check it for every combination of true/false for every single ternary in the statement. You'd have a combinatorial explosion.
The current behavior is, I suspect, the best we're going to get.
Reacted by Titian Cernicova-DragomirI see. That is a bummer, but thanks for looking into it!
- locked as resolved and limited conversation to collaborators
on Oct 21, 2025
TypeScript Version: 3.4.1
Search Terms:
ternary statement vs if statement
ternary statement type widening
Code
Expected behavior:
It appears that returning a value from a ternary statement has a different behavior than and if-statement. The ternary statement type is widened and resolved before returning. This causes some unsafe type behavior. I would expect the two programs above to work the same and throw and error because the type definition of the function returns a number and yet we are definitely returning undefined.
Actual behavior:
The entire ternary statement returns as an "any" type.
Playground Link: playground link
Related Issues: