List invariance seems to be interpreted inconsistently #11543
Replies: 1 comment
|
Your last paragraph is the right answer — "whatever that inference is, the same thing happens with Without a declared type to work from, that call infers as you'd expect: reveal_type(Type2(['foo'])) # Type2[str]
So it's not that invariance is stricter in the second case. It's that the second case never gets the chance to infer Your dogs: list[Dog] = [Dog()]
animals: list[Animal] = dogs
# error: "list[Dog]" is not assignable to "list[Animal]"
# Type parameter "_T@list" is invariantWhich is what you suspected when you said making it explicitly I poked at where the boundary actually sits, on pyright 1.1.411: @dataclass
class Bare[T]: attr: T
@dataclass
class Nested[T]: attr: list[T]
n1: Nested[str | Other] = Nested(['foo']) # ok - not a union
b1: Bare[str | Other] = Bare('foo') # ok
b2: Bare[str | Other] | int = Bare('foo') # ok - union, bare T
b3: Bare[list[str] | Other] | int = Bare(['foo']) # ok - union, list argument
n2: Nested[str | Other] | int = Nested(['foo']) # ERROR
n3: int | Nested[str | Other] = Nested(['foo']) # ERROR - order irrelevant
n4: Nested[str | Other] | None = Nested(['foo']) # ok - OptionalTwo things worth pulling out. A bare If you want it to work as written, give the inference something to hold onto: lit: list[str | Type1[str]] = ['foo']
e: Type2[str | Type1[str]] | str = Type2(lit) # ok
f: Type2[str | Type1[str]] | str = Type2[str | Type1[str]](['foo']) # okThe second is the one I'd reach for — it says exactly what you mean at the call site and doesn't need a temporary. Whether the union case ought to propagate the expected type inward the way the non-union case does is a design question rather than something I can answer from the outside, and the |
Uh oh!
There was an error while loading. Please reload this page.
I think the simplest way to explain this is with an example:
In this example,
ais acceptable according to Pylance/pyright, howeverbis not:It seems that although pyright is happy that, without the union,
list[str]can be assigned tolist[str | SomeOtherType], once the union is included it is not. Most explanations of invariance that I have read suggest thatlist[str]is not a subtype oflist[str | SomeOtherType], however when I try the following I get no issues:I'm still grappling with the concept of invariance personally but it does seem that the inference of the type of
[Dog()]is somehow not inconsistent withlist[Animal], however if I were to make it explicitlylist[Dog], I will get an error. Whatever that inference is, the same thing happens withType2(['foo']), but ONLY when the type doesn't include the union. In the latter example, it is inferred differently.Or at least, that's what I think is happening.
All reactions