Added special-case bidirectional type inference for the right operand of the "|" and "|=" operators (with an expected type based on the left operand). This supports the case where the left operand is a TypedDict and the right operand is a dict expression that conforms to the TypedDict type.

This commit is contained in:
Eric Traut 2022-01-28 12:10:53 -08:00
parent 4d00d36513
commit 9ae9cb7a9a
3 changed files with 43 additions and 1 deletions

View File

@ -10563,6 +10563,10 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
// For the "+" operator , use this technique only if the right operand is
// a list expression. This heuristic handles the common case of `my_list + [0]`.
expectedOperandType = leftType;
} else if (node.operator === OperatorType.BitwiseOr) {
// If this is a bitwise or ("|"), use the type of the left operand. This allows
// us to support the case where a TypedDict is being updated with a dict expression.
expectedOperandType = leftType;
}
}
@ -10732,7 +10736,15 @@ export function createTypeEvaluator(importLookup: ImportLookup, evaluatorOptions
const leftTypeResult = getTypeOfExpression(node.leftExpression);
const leftType = leftTypeResult.type;
const rightTypeResult = getTypeOfExpression(node.rightExpression);
let expectedOperandType: Type | undefined;
if (node.operator === OperatorType.BitwiseOrEqual) {
// If this is a bitwise or ("|="), use the type of the left operand. This allows
// us to support the case where a TypedDict is being updated with a dict expression.
expectedOperandType = leftType;
}
const rightTypeResult = getTypeOfExpression(node.rightExpression, expectedOperandType);
const rightType = rightTypeResult.type;
const isIncomplete = !!rightTypeResult.isIncomplete || !!leftTypeResult.isIncomplete;

View File

@ -0,0 +1,24 @@
# This sample tests the handling of the "|" and "|=" operators
# for TypedDicts.
from typing import TypedDict
class Person(TypedDict, total=False):
name: str
age: int
person: Person = {}
person.update({"name": "Michael"})
person |= {"name": "Michael"}
person = person | {"name": "Michael"}
# This should generate an error.
person |= {"name": "Michael", "other": 1}
# This should generate an error.
person = person | {"name": 1}

View File

@ -1107,3 +1107,9 @@ test('TypedDict16', () => {
TestUtils.validateResults(analysisResults, 9);
});
test('TypedDict17', () => {
const analysisResults = TestUtils.typeAnalyzeSampleFiles(['typedDict17.py']);
TestUtils.validateResults(analysisResults, 2);
});