This came up for platform.uname_result which is a bit hacky. On 3.9+ it pretends to be a six-field named tuple, but one of the fields is lazily evaluated and not included in the constructor.
In this typeshed MR, I attempted to model this behavior like this:
Mypy has the special case error message Class has two incompatible bases derived from tuple, while pyright gives the slightly more generic Base classes of uname_result are mutually incompatible.
Once the proper ignores are placed on it, the more significant divergence is that mypy follows the tuple[str, str, str, str, str, str] base class and allows access to the sixth field, while pyright gives Index 5 is out of range for type uname_result.
I can find a different solution, but mypy’s behavior in this case seems useful. I brought the issue here to see if any consensus might exist about that.
Does this need to be typed as a NamedTuple? I think you can get around the issues here by typing it as a protocol that describes the structure of the type the user receiving this can rely on since it isn’t meant to be a user-constructed type.
I don’t think we should be standardizing what happens with a type ignore. Anything written with a type ignore is already outside of the type system for a reason. I like what Michael mentioned above with the protocol here. You can also inherit from tuple[str, ...] and add properties
You can add a constructor without a type ignore if it is important to be user constructed this way. This method is able to retain that it is a tuple of strings if that’s important for existing code.
You could even make this act as a 6-tuple if you type the constructor adequately to cover it, it’s only making it a NamedTuple that fails because those have a predefined construction.
It’s better to type accurately with some information loss than add a type ignore. It’s a difference between creating unsoundness and leaving what can’t be expressed about something out of what is declared for type-safe use.