Support typeof and instanceof

This commit is contained in:
Anders Hejlsberg
2019-10-05 13:08:24 -07:00
parent 464d1a74c7
commit f7b22a0216
2 changed files with 13 additions and 5 deletions

View File

@@ -856,9 +856,8 @@ namespace ts {
function isNarrowableReference(expr: Expression): boolean {
return expr.kind === SyntaxKind.Identifier || expr.kind === SyntaxKind.ThisKeyword || expr.kind === SyntaxKind.SuperKeyword ||
(isPropertyAccessExpression(expr) || isNonNullExpression(expr) || isParenthesizedExpression(expr)) && isNarrowableReference(expr.expression) ||
isElementAccessExpression(expr) &&
isStringOrNumericLiteralLike(expr.argumentExpression) &&
isNarrowableReference(expr.expression);
isElementAccessExpression(expr) && isStringOrNumericLiteralLike(expr.argumentExpression) && isNarrowableReference(expr.expression) ||
isOptionalChain(expr);
}
function hasNarrowableArgument(expr: CallExpression) {

View File

@@ -19360,7 +19360,7 @@ namespace ts {
if (isMatchingReference(reference, right)) {
return narrowTypeByEquality(type, operator, left, assumeTrue);
}
if (assumeTrue && (operator === SyntaxKind.EqualsEqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken)) {
if (assumeTrue && strictNullChecks) {
if (optionalChainContainsReference(left, reference)) {
type = narrowTypeByOptionalChainContainment(type, operator, right);
}
@@ -19397,7 +19397,9 @@ namespace ts {
// the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the
// operator is !== and the type of value is undefined.
const valueType = getTypeOfExpression(value);
return operator === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) ||
return operator === SyntaxKind.EqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefinedOrNull) ||
operator === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) ||
operator === SyntaxKind.ExclamationEqualsToken && valueType.flags & TypeFlags.Nullable ||
operator === SyntaxKind.ExclamationEqualsEqualsToken && valueType.flags & TypeFlags.Undefined ?
getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
}
@@ -19452,6 +19454,10 @@ namespace ts {
// We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands
const target = getReferenceCandidate(typeOfExpr.expression);
if (!isMatchingReference(reference, target)) {
if (assumeTrue && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken) &&
strictNullChecks && optionalChainContainsReference(target, reference)) {
return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
}
// For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the
// narrowed type of 'y' to its declared type.
if (containsMatchingReference(reference, target)) {
@@ -19633,6 +19639,9 @@ namespace ts {
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
const left = getReferenceCandidate(expr.left);
if (!isMatchingReference(reference, left)) {
if (assumeTrue && strictNullChecks && optionalChainContainsReference(left, reference)) {
return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
}
// For a reference of the form 'x.y', an 'x instanceof T' type guard resets the
// narrowed type of 'y' to its declared type. We do this because preceding 'x.y'
// references might reference a different 'y' property. However, we make an exception