@@ -18787,6 +18787,14 @@ namespace ts {
1878718787 signature.declaration && (getReturnTypeFromAnnotation(signature.declaration) || unknownType).flags & TypeFlags.Never);
1878818788 }
1878918789
18790+ function getTypePredicateArgument(predicate: TypePredicate, callExpression: CallExpression) {
18791+ if (predicate.kind === TypePredicateKind.Identifier || predicate.kind === TypePredicateKind.AssertsIdentifier) {
18792+ return callExpression.arguments[predicate.parameterIndex];
18793+ }
18794+ const invokedExpression = skipParentheses(callExpression.expression);
18795+ return isAccessExpression(invokedExpression) ? skipParentheses(invokedExpression.expression) : undefined;
18796+ }
18797+
1879018798 function reportFlowControlError(node: Node) {
1879118799 const block = <Block | ModuleBlock | SourceFile>findAncestor(node, isFunctionOrModuleBlock);
1879218800 const sourceFile = getSourceFileOfNode(node);
@@ -19338,6 +19346,9 @@ namespace ts {
1933819346 if (isMatchingReference(reference, expr)) {
1933919347 return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
1934019348 }
19349+ if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) {
19350+ type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
19351+ }
1934119352 if (isMatchingReferenceDiscriminant(expr, declaredType)) {
1934219353 return narrowTypeByDiscriminant(type, <AccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
1934319354 }
@@ -19422,21 +19433,13 @@ namespace ts {
1942219433 }
1942319434
1942419435 function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
19425- const op = assumeTrue ? operator :
19426- operator === SyntaxKind.EqualsEqualsToken ? SyntaxKind.ExclamationEqualsToken :
19427- operator === SyntaxKind.EqualsEqualsEqualsToken ? SyntaxKind.ExclamationEqualsEqualsToken :
19428- operator === SyntaxKind.ExclamationEqualsToken ? SyntaxKind.EqualsEqualsToken :
19429- operator === SyntaxKind.ExclamationEqualsEqualsToken ? SyntaxKind.EqualsEqualsEqualsToken :
19430- operator;
1943119436 // We are in a branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from
1943219437 // the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the
1943319438 // operator is !== and the type of value is undefined.
19434- const valueType = getTypeOfExpression(value);
19435- return op === SyntaxKind.EqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefinedOrNull) ||
19436- op === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) ||
19437- op === SyntaxKind.ExclamationEqualsToken && valueType.flags & TypeFlags.Nullable ||
19438- op === SyntaxKind.ExclamationEqualsEqualsToken && valueType.flags & TypeFlags.Undefined ?
19439- getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
19439+ const effectiveTrue = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken ? assumeTrue : !assumeTrue;
19440+ const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
19441+ const valueNonNullish = !(getTypeFacts(getTypeOfExpression(value)) & (doubleEquals ? TypeFacts.EQUndefinedOrNull : TypeFacts.EQUndefined));
19442+ return effectiveTrue === valueNonNullish ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
1944019443 }
1944119444
1944219445 function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
@@ -19487,10 +19490,12 @@ namespace ts {
1948719490
1948819491 function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type {
1948919492 // We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands
19493+ if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
19494+ assumeTrue = !assumeTrue;
19495+ }
1949019496 const target = getReferenceCandidate(typeOfExpr.expression);
1949119497 if (!isMatchingReference(reference, target)) {
19492- if (assumeTrue && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken) &&
19493- strictNullChecks && optionalChainContainsReference(target, reference)) {
19498+ if (strictNullChecks && optionalChainContainsReference(target, reference) && assumeTrue === (literal.text !== "undefined")) {
1949419499 return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
1949519500 }
1949619501 // For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the
@@ -19500,9 +19505,6 @@ namespace ts {
1950019505 }
1950119506 return type;
1950219507 }
19503- if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
19504- assumeTrue = !assumeTrue;
19505- }
1950619508 if (type.flags & TypeFlags.Any && literal.text === "function") {
1950719509 return type;
1950819510 }
@@ -19763,28 +19765,16 @@ namespace ts {
1976319765
1976419766 function narrowTypeByTypePredicate(type: Type, predicate: TypePredicate, callExpression: CallExpression, assumeTrue: boolean): Type {
1976519767 // Don't narrow from 'any' if the predicate type is exactly 'Object' or 'Function'
19766- if (isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType)) {
19767- return type;
19768- }
19769- if (predicate.kind === TypePredicateKind.Identifier || predicate.kind === TypePredicateKind.AssertsIdentifier) {
19770- const predicateArgument = callExpression.arguments[predicate.parameterIndex];
19771- if (predicateArgument && predicate.type) {
19768+ if (predicate.type && !(isTypeAny(type) && (predicate.type === globalObjectType || predicate.type === globalFunctionType))) {
19769+ const predicateArgument = getTypePredicateArgument(predicate, callExpression);
19770+ if (predicateArgument) {
1977219771 if (isMatchingReference(reference, predicateArgument)) {
1977319772 return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
1977419773 }
19775- if (containsMatchingReference(reference, predicateArgument)) {
19776- return declaredType;
19777- }
19778- }
19779- }
19780- else {
19781- const invokedExpression = skipParentheses(callExpression.expression);
19782- if (isAccessExpression(invokedExpression) && predicate.type) {
19783- const possibleReference = skipParentheses(invokedExpression.expression);
19784- if (isMatchingReference(reference, possibleReference)) {
19785- return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
19774+ if (strictNullChecks && assumeTrue && !(getTypeFacts(predicate.type) & TypeFacts.EQUndefined)) {
19775+ type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
1978619776 }
19787- if (containsMatchingReference(reference, possibleReference )) {
19777+ if (containsMatchingReference(reference, predicateArgument )) {
1978819778 return declaredType;
1978919779 }
1979019780 }
0 commit comments