Skip to content

Type of conditional expression is join instead of union #3487

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
JukkaL opened this issue Jun 1, 2017 · 9 comments
Closed

Type of conditional expression is join instead of union #3487

JukkaL opened this issue Jun 1, 2017 · 9 comments

Comments

@JukkaL
Copy link
Collaborator

JukkaL commented Jun 1, 2017

The type of expression such as 1 if foo else 'x' is object (join of int and str). This is surprising and if such an expression is used in a union type context, the result is often not what's expected. Example:

from typing import Union

def f(x: Union[int, str]) -> None: ...

c = 1
f(1 if c else 'x')  # Incompatible argument type "object"

This is also arguably inconsistent, as the or and and operators produce union types. For example, the type of (1+2) or 'x' is Union[int, str].

Maybe conditional expressions should also produce unions? An alternative idea is to only produce union types if the type context is a union type, but this would be somewhat ad-hoc.

@JukkaL
Copy link
Collaborator Author

JukkaL commented Jun 1, 2017

cc @carljm

@adsharma
Copy link

Comment related to #2464. The following shouldn't produce an error either.

class A:
    pass

class B:
    pass

class C:
    def __init__(self, a: A, b: B) -> None:
        if a and b:
            raise Exception("both a and b specified")

        if not a and not b:
            raise Exception("neither a nor b specified")

        self._obj: Union[A, B] = a if a else b
        reveal_type(a if a else b)

If A and B have a common ancestor, it makes sense to use it. But if it turns out to be builtins.object it's probably a good signal that we have a NamedUnion type of case where there are a couple of unrelated objects. So resolving to Union[A, B] makes more sense for that case?

@JukkaL
Copy link
Collaborator Author

JukkaL commented Nov 30, 2017

Bumped priority to high since the workaround is non-trivial and this seems to be affecting multiple users.

@MentalMegalodon
Copy link
Contributor

I'm working on this.

@SKHecht
Copy link

SKHecht commented Jun 6, 2019

So is the new behavior expected to be that the types of conditional expressions are unions instead of joins? I'm not seeing that. I'm on mypy 0.701 and when I run this code in mypy

class A:
    pass
class B:
    pass

def condition() -> bool:
    return True

reveal_type(A() if condition() else B())

I get

error: Revealed type is 'builtins.object'

Based on my understanding of the resolution of this issue I'd expect the type to Union[A, B]

@emmatyping
Copy link
Member

@SKHecht no. It originally was changed to make them Unions instead of joins, but with #5095 this was partially rolled back.

@ilevkivskyi
Copy link
Member

It is still a union in union context.

@cjolowicz
Copy link
Contributor

cjolowicz commented Dec 4, 2019

Should this be reopened?

Here is another use case:

s: str = "key=value"
a, b = s.split("=", 1) if "=" in s else ("key", "default")
# main.py:2: error: 'builtins.object' object is not iterable

This happens because str.split returns List[str] while the else branch has the type Tuple[str].

@JukkaL
Copy link
Collaborator Author

JukkaL commented Dec 4, 2019

@cjolowicz Can you open a separate follow-up issue about your use case?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants