Added support for type narrowing conditional expressions of the form a is False, a is True, a is not False and a is not True.

This commit is contained in:
Eric Traut 2021-06-18 17:01:59 -07:00
parent 7d24e5394c
commit 0440272219
3 changed files with 27 additions and 6 deletions

View File

@ -150,7 +150,7 @@ In addition to assignment-based type narrowing, Pyright supports the following t
* `x is None` and `x is not None`
* `x == None` and `x != None`
* `type(x) is T` and `type(x) is not T`
* `x is E` and `x is not E` (where E is an enum value)
* `x is E` and `x is not E` (where E is an enum value or True or False)
* `x == L` and `x != L` (where L is a literal expression)
* `x.y == L` and `x.y != L` (where L is a literal expression and x is a type that is distinguished by a field with a literal type)
* `x[K] == V` and `x[K] != V` (where K and V are literal expressions and x is a type that is distinguished by a TypedDict field with a literal type)

View File

@ -17573,13 +17573,14 @@ export function createTypeEvaluator(
}
}
// Look for "X is Y" or "X is not Y" where Y is a an enum.
// Look for "X is Y" or "X is not Y" where Y is a an enum or False or True.
if (isOrIsNotOperator) {
if (ParseTreeUtils.isMatchingExpression(reference, testExpression.leftExpression)) {
const rightType = getTypeOfExpression(testExpression.rightExpression).type;
if (
isObject(rightType) &&
ClassType.isEnumClass(rightType.classType) &&
(ClassType.isEnumClass(rightType.classType) ||
ClassType.isBuiltIn(rightType.classType, 'bool')) &&
rightType.classType.literalValue !== undefined
) {
return (type: Type) => {

View File

@ -1,9 +1,9 @@
# This sample tests the type narrowing logic for
# enum values that are compared using the "is" and
# "is not" operators.
# enum values or False/True that are compared using the
# "is" and "is not" operators.
from enum import Enum
from typing import NoReturn
from typing import Literal, NoReturn, Union
class SomeEnum(Enum):
@ -49,3 +49,23 @@ def func4(a: SomeEnum):
# This should generate an error because
# a hasn't been narrowed to Never.
assert_never(a)
def func5(a: Union[str, Literal[False]]) -> str:
if a is False:
return "no"
return a
def func6(a: Union[str, Literal[False]]) -> str:
if a is not False:
return a
return "no"
def func7(a: Union[str, bool]) -> str:
if a is False:
return "False"
elif a is True:
return "True"
return a