microsoft / microsoft/pyright

`TypeVar` with `Never` as an option and unexpected `reportArgumentType`

Open
#11,736 0 comments 0 reactions 0 assignees View on GitHub
bug
Dominant language
Python
Stars
15.6k
Forks
1.8k
Avg merge
12h 13m
Merged PRs (30d)
52

Description

**Describe the bug**

Given `_Z = TypeVar('_Z', SomeType, Never)`, and `class SomeClass(Generic[_Z])`, you'd expect a function parameter of type `SomeClass[_Z]` would accept `SomeClass[Never]`. It does not.

**Code or Screenshots**
Code sample in [pyright playground](https://pyright-play.net/?code=GYJw9gtgBALgngBwJYDsDmUkQWEMoDiApikSEgMYA0UAckQG5k0AqiRAagIYgBQvAfQBaUALxQ2CTjwAUAcmFyaqGDXpMQASn4UANlwDOBqAGVIRAML6jM4qXIUA2sIC6mgFxQAdD94ATImAoA3MBYABXFAoZAU8zCEtrA2chNygAWgA%2BOjBSTx8vflQDGC4oogEVMVNzK0NklRcZbWLS8oFSDWr4xPrHdTIm7V4QhLDI6NayigqVTSgoAGIoMABrKBkUMBXwmARdmkMoIgAPKQoYIj9tUYqIqJkp9s6ybQX39%2BXI0-PLv2OQOAQABCKAIODkNAAC3wBi4cAM7l4Hw%2ByzIQM8AEEQGhwgkUPgwEF4FIoAAiHp1Iz9RiDMlQChlLb4ABGRCg9SQaFIfmRKIWyw%2BMG2CB4XASlxA5IE9KJsHY5MpSRSAAFbuMoi56ag%2BfzBe97hckLlyerDWTdSjlhTasqBiAtZhjMyOUYuSguCzdOzhYrbX1hGrQoatZbURIFaKQOKiJLpUIVUr6trjKgGDwkGVVFAWbtyfaU2HPiiXTAoey4QlXfGg2NzRsQEQcHhsbj8TBJERNEA)

```python
from typing import Generic, Never, TypeVar

_Z = TypeVar('_Z', int, Never)

class SomeClass(Generic[_Z]): ...
def some_func(_: SomeClass[_Z]) -> None: ...

instance_int = SomeClass[int]()
instance_never = SomeClass[Never]()

some_func(instance_int) # ok (no output, as expected)
some_func(instance_never)
# unexpected error! pyright says:
# error: Argument of type "SomeClass[Never]" cannot be assigned
# to parameter "_" of type "SomeClass[_Z@some_func]" in
# function "some_func"
# "SomeClass[Never]" is not assignable to "SomeClass[_Z@some_func]"
# Type parameter "_Z@SomeClass" is invariant, but "Never" is
# not the same as "_Z@some_func" (reportArgumentType)
```

**If this is intended behavior...**
What's the best workaround for a class which may or may not have (for example) a special-case placeholder value?
```python
class SomeClass(Generic[_Z]):
def foo_to_bar(self, value: float) -> int | _Z: ...
def bar_to_foo(self, value: int | _Z) -> float: ...
```
Concrete example: a monotonic mapping from non-negative floats to another linear float space might be the identity function (`lambda x: x`) or might be `log()`/`exp()` with a special case for zero.

**VS Code extension or command-line**
Tested with latest (1.1.414) on pyright-play.net, and via LSP.

Contributor guide

Open the contributing guide

Research direction

Start by running the linked pyright playground sample with version 1.1.414, then reproduce it through the LSP as reported. Trace how the checker handles the constrained TypeVar with Never when checking SomeClass[Never] against some_func, and consider the issue resolved when that call no longer produces reportArgumentType while the existing SomeClass[int] behavior remains correct.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.