Skip to content

Self should be solved to an intersection type in the context of a method called on that intersection type #3565

@AlexWaygood

Description

@AlexWaygood

Summary

Consider the following snippet:

from ty_extensions import Intersection
from typing import Self, reveal_type

class Bar:
    def method(self) -> Self:
        return self

class Foo: ...

def f(x: Intersection[Bar, Foo]):
    reveal_type(x.method())

Currently we reveal Bar on the last line, but I think we should reveal Bar & Foo instead. Methods that are annotated as returning Self at runtime almost always have bodies with return self inside them -- and self in this context must be an instance of both Foo and Bar.

This starts to cause false positives on codebases such as psycopg if we merge astral-sh/ruff#24761, due to snippets like this in psycopg:

from typing import NamedTuple, Any, Sequence, Protocol

class RowMaker[Row: tuple[Any, ...]](Protocol):
    def __call__(self, values: Sequence[Any], /) -> Row: ...

def namedtuple_row(namedtuple_cls: type[NamedTuple]) -> RowMaker[NamedTuple]:
    # error[invalid-return-type] Return type does not match returned value: expected `RowMaker[tuple[object, ...] & NamedTupleLike]`, found `bound method type[NamedTupleLike]._make(iterable: Iterable[Any]) -> NamedTupleLike`
    return namedtuple_cls._make

Version

No response

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions