Repository navigation
Closed
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Description
TypeScript Version: Nightly
Search Terms: Union property does not exist
Code
interface SomeOptions {
foo: boolean;
}
interface OtherOptions {
bar: boolean;
baz?: number;
}
function doSomething(options: SomeOptions | OtherOptions) {
// Error:
const baz1 = options.baz ?? 1;
// Workaround:
const baz2 = ("baz" in options ? options.baz : undefined) ?? 1;
}Expected behavior:
I can access options.baz and its type is number | undefined.
Actual behavior:
Error: property baz does not exist on type SomeOptions. The workaround does work, but is unnecessarily verbose and does unnecessary runtime checks.
I know this is mentioned in the handbook, but I'd like to advocate for this behavior to be reconsidered. It was mentioned that the type at runtime might be different than expected, but this is also true for the workaround.
I also know I could add all these properties to the other union members (baz?: never), but this is quite tedious and error prone for complex real-world types.
Related Issues:
- Union of types contains only the keys that exist in both subtypes #35224 is arguing solely on the type level, doesn't seem too relevant to this use case
- Consider property access a form of type guards #1260 (comment) - Daniel Rosenwasser (@DanielRosenwasser)'s comment seems to be pretty close to what I'm proposing here. Now that we have conditional chaining, the example could even go one step further:
if (foo.bar?.bizzle) { /* this is fine */ } - "Property does not exist" error on union type even though all union members implement interface #26204 - this was closed as "working as intended" because it relied on the implementation of interfaces
Reacted by rrain0
Metadata
Metadata
Assignees
Labels
Working as IntendedThe behavior described is the intended behavior; this is not a bugThe behavior described is the intended behavior; this is not a bug
Activity
[-]Cannot access property that exists on some union members[/-][+]Cannot access property that exists only on some union members[/+]jcalz commentedon Jan 15, 2020
Related to the declined #33736.
AlCalzone commentedon Jan 15, 2020
Joe Calzaretta (@jcalz) The OP there is actually quite similar - but the issue has been closed because the control flow narrowing effects are undesired. My intent here is just to get the non-chained property access.
I've read in other issues that this behavior requires exact types (so we cannot pass a
SomeOptions & {baz: boolean}for example). In that case, please put this request on the wishlist for that feature.RyanCavanaugh commentedon Jan 15, 2020
This has been the behavior for about five years now; #1260 would be the issue to follow if you expect changes.
AlCalzone commentedon Jan 15, 2020
Ryan Cavanaugh (@RyanCavanaugh) is it realistic to expect such a change (for example in combination with exact types) at some point in the future?
RyanCavanaugh commentedon Jan 16, 2020
We'd have to think about it. Just hypothesizing, let's say you had
What would be the ideal rules here?
ab.ain all positions would be kind of bonkers. It would imply you could write pure nonsense likeab.a?.toLowerCase() + ab.x?.toFixed()ab.ais only allowable sometimes. But where?|/||/&/&&)"a" in abis what we have todayAlCalzone commentedon Jan 16, 2020
I have no good answer for that. The example you posted is clearly much more complicated than my simple use case of a single property access.
typescript-bot commentedon Jan 18, 2020
This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.
patrickjcurran commentedon Apr 19, 2023
I disagree that that is bonkers. The question mark makes it clear that either the left or right term might be undefined. Yes, we are loosing the fact that precisely one of them might be undefined, but that seems like a fair tradeoff.
For example if you allow access to anything defined in the union, but as optional properties:
You will still get an error
Object is possibly 'undefined'.because the left and right terms might be undefined.AlCalzone commentedon Apr 19, 2023
Patrick Curran (@patrickjcurran) the error is because you're trying to concatenate possibly undefined values:
patrickjcurran commentedon Apr 19, 2023
AlCalzone understood, my point was that it is good that the type checker doesn't allow that. I'll edit my post to make that more clear. Thank you.
rrain0 commentedon Mar 3, 2025
It is not an ultimate solution, but i've created a workaround that can cover some cases. Hope this helps someone:
And my case with optional props of React component: