@@ -13572,10 +13572,27 @@ namespace ts {
13572
13572
if (!switchWitnesses.length) {
13573
13573
return type;
13574
13574
}
13575
- const clauseWitnesses = switchWitnesses.slice(clauseStart, clauseEnd);
13576
13575
// Equal start and end denotes implicit fallthrough; undefined marks explicit default clause
13577
- const hasDefaultClause = clauseStart === clauseEnd || contains(clauseWitnesses, /*explicitDefaultStatement*/ undefined);
13578
- const switchFacts = getFactsFromTypeofSwitch(clauseStart, clauseEnd, switchWitnesses, hasDefaultClause);
13576
+ const defaultCaseLocation = findIndex(switchWitnesses, elem => elem === undefined);
13577
+ const hasDefaultClause = clauseStart === clauseEnd || (defaultCaseLocation >= clauseStart && defaultCaseLocation < clauseEnd);
13578
+ let clauseWitnesses: string[];
13579
+ let switchFacts: TypeFacts;
13580
+ if (defaultCaseLocation > -1) {
13581
+ // We no longer need the undefined denoting an
13582
+ // explicit default case. Remove the undefined and
13583
+ // fix-up clauseStart and clauseEnd. This means
13584
+ // that we don't have to worry about undefined
13585
+ // in the witness array.
13586
+ const witnesses = <string[]>switchWitnesses.filter(witness => witness !== undefined);
13587
+ const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart;
13588
+ const fixedClauseEnd = defaultCaseLocation < clauseEnd ? clauseEnd - 1 : clauseEnd;
13589
+ clauseWitnesses = witnesses.slice(fixedClauseStart, fixedClauseEnd);
13590
+ switchFacts = getFactsFromTypeofSwitch(fixedClauseStart, fixedClauseEnd, witnesses, hasDefaultClause);
13591
+ }
13592
+ else {
13593
+ clauseWitnesses = <string[]>switchWitnesses.slice(clauseStart, clauseEnd);
13594
+ switchFacts = getFactsFromTypeofSwitch(clauseStart, clauseEnd, <string[]>switchWitnesses, hasDefaultClause);
13595
+ }
13579
13596
// The implied type is the raw type suggested by a
13580
13597
// value being caught in this clause.
13581
13598
// - If there is a default the implied type is not used.
@@ -13594,10 +13611,10 @@ namespace ts {
13594
13611
// }
13595
13612
//
13596
13613
// The implied type of the first clause number | string.
13597
- // The implied type of the second clause is never, but this does not get just because it includes a default case.
13614
+ // The implied type of the second clause is never, but this does not get used because it includes a default case.
13598
13615
// The implied type of the third clause is boolean (number has already be caught).
13599
13616
if (!(hasDefaultClause || (type.flags & TypeFlags.Union))) {
13600
- let impliedType = getTypeWithFacts(getUnionType((<string[]> clauseWitnesses) .map(text => typeofTypesByName.get(text) || neverType)), switchFacts);
13617
+ let impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => typeofTypesByName.get(text) || neverType)), switchFacts);
13601
13618
if (impliedType.flags & TypeFlags.Union) {
13602
13619
impliedType = getAssignmentReducedType(impliedType as UnionType, getBaseConstraintOfType(type) || type);
13603
13620
}
@@ -19019,43 +19036,38 @@ namespace ts {
19019
19036
* from `start` to `end`. Parameter `hasDefault` denotes
19020
19037
* whether the active clause contains a default clause.
19021
19038
*/
19022
- function getFactsFromTypeofSwitch(start: number, end: number, witnesses: ( string | undefined) [], hasDefault: boolean): TypeFacts {
19039
+ function getFactsFromTypeofSwitch(start: number, end: number, witnesses: string[], hasDefault: boolean): TypeFacts {
19023
19040
let facts: TypeFacts = TypeFacts.None;
19024
19041
// When in the default we only collect inequality facts
19025
19042
// because default is 'in theory' a set of infinite
19026
19043
// equalities.
19027
19044
if (hasDefault) {
19028
19045
// Value is not equal to any types after the active clause.
19029
19046
for (let i = end; i < witnesses.length; i++) {
19030
- const witness = witnesses[i];
19031
- facts |= (witness && typeofNEFacts.get(witness)) || TypeFacts.TypeofNEHostObject;
19047
+ facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject;
19032
19048
}
19033
19049
// Remove inequalities for types that appear in the
19034
19050
// active clause because they appear before other
19035
19051
// types collected so far.
19036
19052
for (let i = start; i < end; i++) {
19037
- const witness = witnesses[i];
19038
- facts &= ~((witness && typeofNEFacts.get(witness)) || 0);
19053
+ facts &= ~(typeofNEFacts.get(witnesses[i]) || 0);
19039
19054
}
19040
19055
// Add inequalities for types before the active clause unconditionally.
19041
19056
for (let i = 0; i < start; i++) {
19042
- const witness = witnesses[i];
19043
- facts |= (witness && typeofNEFacts.get(witness)) || TypeFacts.TypeofNEHostObject;
19057
+ facts |= typeofNEFacts.get(witnesses[i]) || TypeFacts.TypeofNEHostObject;
19044
19058
}
19045
19059
}
19046
19060
// When in an active clause without default the set of
19047
19061
// equalities is finite.
19048
19062
else {
19049
19063
// Add equalities for all types in the active clause.
19050
19064
for (let i = start; i < end; i++) {
19051
- const witness = witnesses[i];
19052
- facts |= (witness && typeofEQFacts.get(witness)) || TypeFacts.TypeofEQHostObject;
19065
+ facts |= typeofEQFacts.get(witnesses[i]) || TypeFacts.TypeofEQHostObject;
19053
19066
}
19054
19067
// Remove equalities for types that appear before the
19055
19068
// active clause.
19056
19069
for (let i = 0; i < start; i++) {
19057
- const witness = witnesses[i];
19058
- facts &= ~((witness && typeofEQFacts.get(witness)) || 0);
19070
+ facts &= ~(typeofEQFacts.get(witnesses[i]) || 0);
19059
19071
}
19060
19072
}
19061
19073
return facts;
@@ -19067,8 +19079,10 @@ namespace ts {
19067
19079
}
19068
19080
if (node.expression.kind === SyntaxKind.TypeOfExpression) {
19069
19081
const operandType = getTypeOfExpression((node.expression as TypeOfExpression).expression);
19070
- // Type is not equal to every type in the switch.
19071
- const notEqualFacts = getFactsFromTypeofSwitch(0, 0, getSwitchClauseTypeOfWitnesses(node), /*hasDefault*/ true);
19082
+ // This cast is safe because the switch is possibly exhaustive and does not contain a default case, so there can be no undefined.
19083
+ const witnesses = <string[]>getSwitchClauseTypeOfWitnesses(node);
19084
+ // notEqualFacts states that the type of the switched value is not equal to every type in the switch.
19085
+ const notEqualFacts = getFactsFromTypeofSwitch(0, 0, witnesses, /*hasDefault*/ true);
19072
19086
const type = getBaseConstraintOfType(operandType) || operandType;
19073
19087
return !!(filterType(type, t => (getTypeFacts(t) & notEqualFacts) === notEqualFacts).flags & TypeFlags.Never);
19074
19088
}
0 commit comments