Improved isinstance type narrowing logic to retain type arguments in cases where the corresponding type parameter is bound or constrained.

This commit is contained in:
Eric Traut 2021-12-13 16:21:01 -08:00
parent 78f2a94943
commit 01ae3bf97c
3 changed files with 48 additions and 5 deletions

View File

@ -2926,6 +2926,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
return combineTypes(typesToCombine);
}
if (subtype.details.isExemptFromBoundCheck) {
return AnyType.create();
}
// Convert to an "object" or "type" instance depending on whether
// it's instantiable.
if (TypeBase.isInstantiable(subtype)) {
@ -7351,6 +7355,7 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const typeVar = TypeVarType.createInstance(`__source${index}`);
typeVar.details.isSynthesized = true;
typeVar.details.synthesizedIndex = index;
typeVar.details.isExemptFromBoundCheck = true;
return typeVar;
});

View File

@ -1714,6 +1714,7 @@ export interface TypeVarDetails {
isSynthesized: boolean;
isSynthesizedSelf?: boolean | undefined;
synthesizedIndex?: number | undefined;
isExemptFromBoundCheck?: boolean;
// Used for recursive type aliases.
recursiveTypeAliasName?: string | undefined;

View File

@ -2,16 +2,16 @@
# narrow the type of a specialized class to a subclass where the type
# arguments are implied by the type arguments of the wider class.
from typing import Generic, Iterable, Literal, Sequence, Type, TypeVar, Union
from typing import Any, Generic, Iterable, Literal, Sequence, Type, TypeVar, Union
T = TypeVar("T")
_T1 = TypeVar("_T1")
class SomeClass(Generic[T]):
class SomeClass(Generic[_T1]):
...
class OtherClass(SomeClass[T]):
class OtherClass(SomeClass[_T1]):
...
@ -35,6 +35,43 @@ def func2(
)
def func3(value: Iterable[T]) -> Sequence[T] | None:
def func3(value: Iterable[_T1]) -> Sequence[_T1] | None:
if isinstance(value, Sequence):
return value
_T2 = TypeVar("_T2", bound=float, covariant=True)
class Parent1(Generic[_T2]):
pass
class Child1(Parent1[_T2]):
pass
def func4(var: Parent1[int]):
if isinstance(var, Child1):
t1: Literal["Child1[int]"] = reveal_type(var)
def func5(var: Parent1[Any]):
if isinstance(var, Child1):
t1: Literal["Child1[Any]"] = reveal_type(var)
_T3 = TypeVar("_T3", float, str)
class Parent2(Generic[_T3]):
pass
class Child2(Parent2[_T3]):
pass
def func6(var: Parent2[int]):
if isinstance(var, Child2):
t1: Literal["Child2[int]"] = reveal_type(var)