@@ -19175,20 +19175,25 @@ namespace ts {
19175
19175
if (isMatchingReference(reference, expr)) {
19176
19176
type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
19177
19177
}
19178
- else if (isMatchingReferenceDiscriminant(expr, type)) {
19179
- type = narrowTypeByDiscriminant(
19180
- type,
19181
- expr as AccessExpression,
19182
- t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
19183
- }
19184
19178
else if (expr.kind === SyntaxKind.TypeOfExpression && isMatchingReference(reference, (expr as TypeOfExpression).expression)) {
19185
19179
type = narrowBySwitchOnTypeOf(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
19186
19180
}
19187
- else if (containsMatchingReferenceDiscriminant(reference, expr)) {
19188
- type = declaredType;
19189
- }
19190
- else if (flow.clauseStart === flow.clauseEnd && isExhaustiveSwitchStatement(flow.switchStatement)) {
19191
- return unreachableNeverType;
19181
+ else {
19182
+ if (strictNullChecks && optionalChainContainsReference(expr, reference)) {
19183
+ type = narrowTypeBySwitchOptionalChainContainment(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
19184
+ }
19185
+ if (isMatchingReferenceDiscriminant(expr, type)) {
19186
+ type = narrowTypeByDiscriminant(
19187
+ type,
19188
+ expr as AccessExpression,
19189
+ t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
19190
+ }
19191
+ else if (containsMatchingReferenceDiscriminant(reference, expr)) {
19192
+ type = declaredType;
19193
+ }
19194
+ else if (flow.clauseStart === flow.clauseEnd && isExhaustiveSwitchStatement(flow.switchStatement)) {
19195
+ return unreachableNeverType;
19196
+ }
19192
19197
}
19193
19198
return createFlowType(type, isIncomplete(flowType));
19194
19199
}
@@ -19384,12 +19389,12 @@ namespace ts {
19384
19389
if (isMatchingReference(reference, right)) {
19385
19390
return narrowTypeByEquality(type, operator, left, assumeTrue);
19386
19391
}
19387
- if (assumeTrue && strictNullChecks) {
19392
+ if (strictNullChecks) {
19388
19393
if (optionalChainContainsReference(left, reference)) {
19389
- type = narrowTypeByOptionalChainContainment(type, operator, right);
19394
+ type = narrowTypeByOptionalChainContainment(type, operator, right, assumeTrue );
19390
19395
}
19391
19396
else if (optionalChainContainsReference(right, reference)) {
19392
- type = narrowTypeByOptionalChainContainment(type, operator, left);
19397
+ type = narrowTypeByOptionalChainContainment(type, operator, left, assumeTrue );
19393
19398
}
19394
19399
}
19395
19400
if (isMatchingReferenceDiscriminant(left, declaredType)) {
@@ -19416,15 +19421,21 @@ namespace ts {
19416
19421
return type;
19417
19422
}
19418
19423
19419
- function narrowTypeByOptionalChainContainment(type: Type, operator: SyntaxKind, value: Expression): Type {
19420
- // We are in the true branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from
19424
+ 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
+ // We are in a branch of obj?.foo === value or obj?.foo !== value. We remove undefined and null from
19421
19432
// the type of obj if (a) the operator is === and the type of value doesn't include undefined or (b) the
19422
19433
// operator is !== and the type of value is undefined.
19423
19434
const valueType = getTypeOfExpression(value);
19424
- return operator === SyntaxKind.EqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefinedOrNull) ||
19425
- operator === SyntaxKind.EqualsEqualsEqualsToken && !(getTypeFacts(valueType) & TypeFacts.EQUndefined) ||
19426
- operator === SyntaxKind.ExclamationEqualsToken && valueType.flags & TypeFlags.Nullable ||
19427
- operator === SyntaxKind.ExclamationEqualsEqualsToken && valueType.flags & TypeFlags.Undefined ?
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 ?
19428
19439
getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
19429
19440
}
19430
19441
@@ -19526,6 +19537,12 @@ namespace ts {
19526
19537
}
19527
19538
}
19528
19539
19540
+ function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
19541
+ const noClauseIsDefaultOrUndefined = clauseStart !== clauseEnd &&
19542
+ every(getSwitchClauseTypes(switchStatement).slice(clauseStart, clauseEnd), t => !(t.flags & (TypeFlags.Undefined | TypeFlags.Never)));
19543
+ return noClauseIsDefaultOrUndefined ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type;
19544
+ }
19545
+
19529
19546
function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
19530
19547
// We only narrow if all case expressions specify
19531
19548
// values with unit types, except for the case where
0 commit comments