@@ -17511,32 +17511,7 @@ namespace ts {
17511
17511
const facts = assumeTrue ?
17512
17512
typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
17513
17513
typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject;
17514
- return getTypeWithFacts(assumeTrue ? mapType(type, narrowTypeForTypeof) : type, facts);
17515
-
17516
- function narrowTypeForTypeof(type: Type) {
17517
- if (type.flags & TypeFlags.Unknown && literal.text === "object") {
17518
- return getUnionType([nonPrimitiveType, nullType]);
17519
- }
17520
- // We narrow a non-union type to an exact primitive type if the non-union type
17521
- // is a supertype of that primitive type. For example, type 'any' can be narrowed
17522
- // to one of the primitive types.
17523
- const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text);
17524
- if (targetType) {
17525
- if (isTypeSubtypeOf(type, targetType)) {
17526
- return type;
17527
- }
17528
- if (isTypeSubtypeOf(targetType, type)) {
17529
- return targetType;
17530
- }
17531
- if (type.flags & TypeFlags.Instantiable) {
17532
- const constraint = getBaseConstraintOfType(type) || anyType;
17533
- if (isTypeSubtypeOf(targetType, constraint)) {
17534
- return getIntersectionType([type, targetType]);
17535
- }
17536
- }
17537
- }
17538
- return type;
17539
- }
17514
+ return getTypeWithFacts(assumeTrue ? mapType(type, narrowUnionMemberByTypeof(getImpliedTypeFromTypeofGuard(type, literal.text))) : type, facts);
17540
17515
}
17541
17516
17542
17517
function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
@@ -17582,7 +17557,7 @@ namespace ts {
17582
17557
return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]);
17583
17558
}
17584
17559
17585
- function getImpliedTypeFromTypeofCase (type: Type, text: string) {
17560
+ function getImpliedTypeFromTypeofGuard (type: Type, text: string) {
17586
17561
switch (text) {
17587
17562
case "function":
17588
17563
return type.flags & TypeFlags.Any ? type : globalFunctionType;
@@ -17593,8 +17568,17 @@ namespace ts {
17593
17568
}
17594
17569
}
17595
17570
17596
- function narrowTypeForTypeofSwitch(candidate: Type) {
17571
+ // When narrowing a union type by a `typeof` guard using type-facts alone, constituent types that are
17572
+ // super-types of the implied guard will be retained in the final type: this is because type-facts only
17573
+ // filter. Instead, we would like to replace those union constituents with the more precise type implied by
17574
+ // the guard. For example: narrowing `{} | undefined` by `"boolean"` should produce the type `boolean`, not
17575
+ // the filtered type `{}`. For this reason we narrow constituents of the union individually, in addition to
17576
+ // filtering by type-facts.
17577
+ function narrowUnionMemberByTypeof(candidate: Type) {
17597
17578
return (type: Type) => {
17579
+ if (isTypeSubtypeOf(type, candidate)) {
17580
+ return type;
17581
+ }
17598
17582
if (isTypeSubtypeOf(candidate, type)) {
17599
17583
return candidate;
17600
17584
}
@@ -17619,11 +17603,9 @@ namespace ts {
17619
17603
let clauseWitnesses: string[];
17620
17604
let switchFacts: TypeFacts;
17621
17605
if (defaultCaseLocation > -1) {
17622
- // We no longer need the undefined denoting an
17623
- // explicit default case. Remove the undefined and
17624
- // fix-up clauseStart and clauseEnd. This means
17625
- // that we don't have to worry about undefined
17626
- // in the witness array.
17606
+ // We no longer need the undefined denoting an explicit default case. Remove the undefined and
17607
+ // fix-up clauseStart and clauseEnd. This means that we don't have to worry about undefined in the
17608
+ // witness array.
17627
17609
const witnesses = <string[]>switchWitnesses.filter(witness => witness !== undefined);
17628
17610
// The adjusted clause start and end after removing the `default` statement.
17629
17611
const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart;
@@ -17666,11 +17648,8 @@ namespace ts {
17666
17648
boolean. We know that number cannot be selected
17667
17649
because it is caught in the first clause.
17668
17650
*/
17669
- let impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofCase(type, text))), switchFacts);
17670
- if (impliedType.flags & TypeFlags.Union) {
17671
- impliedType = getAssignmentReducedType(impliedType as UnionType, getBaseConstraintOrType(type));
17672
- }
17673
- return getTypeWithFacts(mapType(type, narrowTypeForTypeofSwitch(impliedType)), switchFacts);
17651
+ const impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofGuard(type, text))), switchFacts);
17652
+ return getTypeWithFacts(mapType(type, narrowUnionMemberByTypeof(impliedType)), switchFacts);
17674
17653
}
17675
17654
17676
17655
function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
0 commit comments