@@ -18124,6 +18124,16 @@ namespace ts {
18124
18124
return false;
18125
18125
}
18126
18126
18127
+ function optionalChainContainsReference(source: Node, target: Node) {
18128
+ while (isOptionalChain(source)) {
18129
+ source = source.expression;
18130
+ if (isMatchingReference(source, target)) {
18131
+ return true;
18132
+ }
18133
+ }
18134
+ return false;
18135
+ }
18136
+
18127
18137
// Return true if target is a property access xxx.yyy, source is a property access xxx.zzz, the declared
18128
18138
// type of xxx is a union type, and yyy is a property that is possibly a discriminant. We consider a property
18129
18139
// a possible discriminant if its type differs in the constituents of containing union type, and if every
@@ -19378,6 +19388,14 @@ namespace ts {
19378
19388
if (isMatchingReference(reference, right)) {
19379
19389
return narrowTypeByEquality(type, operator, left, assumeTrue);
19380
19390
}
19391
+ if (assumeTrue && strictNullChecks) {
19392
+ if (optionalChainContainsReference(left, reference)) {
19393
+ type = narrowTypeByOptionalChainContainment(type, operator, right);
19394
+ }
19395
+ else if (optionalChainContainsReference(right, reference)) {
19396
+ type = narrowTypeByOptionalChainContainment(type, operator, left);
19397
+ }
19398
+ }
19381
19399
if (isMatchingReferenceDiscriminant(left, declaredType)) {
19382
19400
return narrowTypeByDiscriminant(type, <AccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
19383
19401
}
@@ -19402,6 +19420,18 @@ namespace ts {
19402
19420
return type;
19403
19421
}
19404
19422
19423
+ function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression): Type {
19424
+ // We are in the true branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from
19425
+ // the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the
19426
+ // operator is !== and the type of value is undefined.
19427
+ const valueType = getTypeOfExpression(value);
19428
+ return operator === SyntaxKind.EqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefinedOrNull) ||
19429
+ operator === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) ||
19430
+ operator === SyntaxKind.ExclamationEqualsToken && valueType.flags & TypeFlags.Nullable ||
19431
+ operator === SyntaxKind.ExclamationEqualsEqualsToken && valueType.flags & TypeFlags.Undefined ?
19432
+ getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
19433
+ }
19434
+
19405
19435
function narrowTypeByEquality(type: Type, operator: SyntaxKind, value: Expression, assumeTrue: boolean): Type {
19406
19436
if (type.flags & TypeFlags.Any) {
19407
19437
return type;
@@ -19452,6 +19482,10 @@ namespace ts {
19452
19482
// We have '==', '!=', '===', or !==' operator with 'typeof xxx' and string literal operands
19453
19483
const target = getReferenceCandidate(typeOfExpr.expression);
19454
19484
if (!isMatchingReference(reference, target)) {
19485
+ if (assumeTrue && (operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.EqualsEqualsEqualsToken) &&
19486
+ strictNullChecks && optionalChainContainsReference(target, reference)) {
19487
+ return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
19488
+ }
19455
19489
// For a reference of the form 'x.y', a 'typeof x === ...' type guard resets the
19456
19490
// narrowed type of 'y' to its declared type.
19457
19491
if (containsMatchingReference(reference, target)) {
@@ -19633,6 +19667,9 @@ namespace ts {
19633
19667
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
19634
19668
const left = getReferenceCandidate(expr.left);
19635
19669
if (!isMatchingReference(reference, left)) {
19670
+ if (assumeTrue && strictNullChecks && optionalChainContainsReference(left, reference)) {
19671
+ return getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull);
19672
+ }
19636
19673
// For a reference of the form 'x.y', an 'x instanceof T' type guard resets the
19637
19674
// narrowed type of 'y' to its declared type. We do this because preceding 'x.y'
19638
19675
// references might reference a different 'y' property. However, we make an exception
@@ -23621,7 +23658,20 @@ namespace ts {
23621
23658
// If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible.
23622
23659
// If the expression is a new expression, then the check is skipped.
23623
23660
const thisArgumentNode = getThisArgumentOfCall(node);
23624
- const thisArgumentType = thisArgumentNode ? checkExpression(thisArgumentNode) : voidType;
23661
+ let thisArgumentType: Type;
23662
+ if (thisArgumentNode) {
23663
+ thisArgumentType = checkExpression(thisArgumentNode);
23664
+ if (isOptionalChainRoot(thisArgumentNode.parent)) {
23665
+ thisArgumentType = getNonNullableType(thisArgumentType);
23666
+ }
23667
+ else if (isOptionalChain(thisArgumentNode.parent)) {
23668
+ thisArgumentType = removeOptionalTypeMarker(thisArgumentType);
23669
+ }
23670
+ }
23671
+ else {
23672
+ thisArgumentType = voidType;
23673
+ }
23674
+
23625
23675
const errorNode = reportErrors ? (thisArgumentNode || node) : undefined;
23626
23676
const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1;
23627
23677
if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) {
0 commit comments