ty icon indicating copy to clipboard operation
ty copied to clipboard

Improve predictability of instance attribute type inference

Open wvanbergen opened this issue 2 months ago • 1 comments

Summary

Unrelated code in a initializer appears to prevent type narrowing. Example:

class Foo:
    def bar(self) -> None:
        # revealed type changes to `Literal[True]` if you comment out any of these lines related to _unrelated
        if hasattr(self, "_unrelated"):
            return
        self._unrelated = True

        self._b: bool | None = None    
        self._b = True
        reveal_type(self._b)  # bool | None, expected Literal[True]

If you comment out any of the _unrelated lines, it changes to Literal[True].

This behaviour seems to be specific to it being an instance method. For a regular function, the type narrowing never happens.

  • Playground recreation: https://play.ty.dev/c43a9722-ed99-4749-92d3-ada996276c66
  • mypy evaluates it as builtins.bool: https://mypy-play.net/?mypy=latest&python=3.12&gist=a2789d2ba9c6b0c511c577bba939e150
  • Running uvx ty check, no ty related settings in pyproject.toml

Version

ty 0.0.5 (d37b7dbd9 2025-12-20)

wvanbergen avatar Dec 23 '25 17:12 wvanbergen

Heh, file this one under "things I did not expect to get an issue about." Lesson learned: everything will be noticed :) Thanks for the report!

Your "unrelated" code is not really entirely unrelated, because of the conditional return. What happens here is that we enter a cycle finding the attributes of instances of Foo, because we flip between thinking the class has two attributes and thinking it has none (because once we think it has _unrelated, then we see the early exit.) We fallback to just going with the declared type, which I think is reasonable.

There are various possible improvements here, though I don't think the current behavior is that much of a problem.

carljm avatar Dec 23 '25 19:12 carljm