@@ -18787,6 +18787,14 @@ namespace ts {
18787
18787
signature.declaration && (getReturnTypeFromAnnotation(signature.declaration) || unknownType).flags & TypeFlags.Never);
18788
18788
}
18789
18789
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
+
18790
18798
function reportFlowControlError(node: Node) {
18791
18799
const block = <Block | ModuleBlock | SourceFile>findAncestor(node, isFunctionOrModuleBlock);
18792
18800
const sourceFile = getSourceFileOfNode(node);
@@ -19338,6 +19346,9 @@ namespace ts {
19338
19346
if (isMatchingReference(reference, expr)) {
19339
19347
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
19340
19348
}
19349
+ if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) {
19350
+ type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
19351
+ }
19341
19352
if (isMatchingReferenceDiscriminant(expr, declaredType)) {
19342
19353
return narrowTypeByDiscriminant(type, <AccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
19343
19354
}
@@ -19422,21 +19433,13 @@ namespace ts {
19422
19433
}
19423
19434
19424
19435
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;
19431
19436
// We are in a branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from
19432
19437
// the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the
19433
19438
// 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;
19440
19443
}
19441
19444
19442
19445
function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
@@ -19487,10 +19490,12 @@ namespace ts {
19487
19490
19488
19491
function narrowTypeByTypeof(type: Type, typeOfExpr: TypeOfExpression, operator: SyntaxKind, literal: LiteralExpression, assumeTrue: boolean): Type {
19489
19492
// We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands
19493
+ if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
19494
+ assumeTrue = !assumeTrue;
19495
+ }
19490
19496
const target = getReferenceCandidate(typeOfExpr.expression);
19491
19497
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")) {
19494
19499
return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
19495
19500
}
19496
19501
// For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the
@@ -19500,9 +19505,6 @@ namespace ts {
19500
19505
}
19501
19506
return type;
19502
19507
}
19503
- if (operator === SyntaxKind.ExclamationEqualsToken || operator === SyntaxKind.ExclamationEqualsEqualsToken) {
19504
- assumeTrue = !assumeTrue;
19505
- }
19506
19508
if (type.flags & TypeFlags.Any && literal.text === "function") {
19507
19509
return type;
19508
19510
}
@@ -19763,28 +19765,16 @@ namespace ts {
19763
19765
19764
19766
function narrowTypeByTypePredicate(type: Type, predicate: TypePredicate, callExpression: CallExpression, assumeTrue: boolean): Type {
19765
19767
// 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) {
19772
19771
if (isMatchingReference(reference, predicateArgument)) {
19773
19772
return getNarrowedType(type, predicate.type, assumeTrue, isTypeSubtypeOf);
19774
19773
}
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);
19786
19776
}
19787
- if (containsMatchingReference(reference, possibleReference )) {
19777
+ if (containsMatchingReference(reference, predicateArgument )) {
19788
19778
return declaredType;
19789
19779
}
19790
19780
}
0 commit comments