@@ -17511,32 +17511,7 @@ namespace ts {
1751117511 const facts = assumeTrue ?
1751217512 typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
1751317513 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);
1754017515 }
1754117516
1754217517 function narrowTypeBySwitchOnDiscriminant(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number) {
@@ -17582,7 +17557,7 @@ namespace ts {
1758217557 return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]);
1758317558 }
1758417559
17585- function getImpliedTypeFromTypeofCase (type: Type, text: string) {
17560+ function getImpliedTypeFromTypeofGuard (type: Type, text: string) {
1758617561 switch (text) {
1758717562 case "function":
1758817563 return type.flags & TypeFlags.Any ? type : globalFunctionType;
@@ -17593,8 +17568,17 @@ namespace ts {
1759317568 }
1759417569 }
1759517570
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) {
1759717578 return (type: Type) => {
17579+ if (isTypeSubtypeOf(type, candidate)) {
17580+ return type;
17581+ }
1759817582 if (isTypeSubtypeOf(candidate, type)) {
1759917583 return candidate;
1760017584 }
@@ -17619,11 +17603,9 @@ namespace ts {
1761917603 let clauseWitnesses: string[];
1762017604 let switchFacts: TypeFacts;
1762117605 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.
1762717609 const witnesses = <string[]>switchWitnesses.filter(witness => witness !== undefined);
1762817610 // The adjusted clause start and end after removing the `default` statement.
1762917611 const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart;
@@ -17666,11 +17648,8 @@ namespace ts {
1766617648 boolean. We know that number cannot be selected
1766717649 because it is caught in the first clause.
1766817650 */
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);
1767417653 }
1767517654
1767617655 function narrowTypeByInstanceof(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
0 commit comments