@@ -20806,29 +20806,8 @@ namespace ts {
2080620806 const facts = assumeTrue ?
2080720807 typeofEQFacts.get(literal.text) || TypeFacts.TypeofEQHostObject :
2080820808 typeofNEFacts.get(literal.text) || TypeFacts.TypeofNEHostObject;
20809- return getTypeWithFacts(assumeTrue ? mapType(type, narrowTypeForTypeof) : type, facts);
20810-
20811- function narrowTypeForTypeof(type: Type) {
20812- // We narrow a non-union type to an exact primitive type if the non-union type
20813- // is a supertype of that primitive type. For example, type 'any' can be narrowed
20814- // to one of the primitive types.
20815- const targetType = literal.text === "function" ? globalFunctionType : typeofTypesByName.get(literal.text);
20816- if (targetType) {
20817- if (isTypeSubtypeOf(type, targetType)) {
20818- return type;
20819- }
20820- if (isTypeSubtypeOf(targetType, type)) {
20821- return targetType;
20822- }
20823- if (type.flags & TypeFlags.Instantiable) {
20824- const constraint = getBaseConstraintOfType(type) || anyType;
20825- if (isTypeSubtypeOf(targetType, constraint)) {
20826- return getIntersectionType([type, targetType]);
20827- }
20828- }
20829- }
20830- return type;
20831- }
20809+ const impliedType = getImpliedTypeFromTypeofGuard(type, literal.text);
20810+ return getTypeWithFacts(assumeTrue && impliedType ? mapType(type, narrowUnionMemberByTypeof(impliedType)) : type, facts);
2083220811 }
2083320812
2083420813 function narrowTypeBySwitchOptionalChainContainment(type: Type, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number, clauseCheck: (type: Type) => boolean) {
@@ -20879,19 +20858,28 @@ namespace ts {
2087920858 return caseType.flags & TypeFlags.Never ? defaultType : getUnionType([caseType, defaultType]);
2088020859 }
2088120860
20882- function getImpliedTypeFromTypeofCase (type: Type, text: string) {
20861+ function getImpliedTypeFromTypeofGuard (type: Type, text: string) {
2088320862 switch (text) {
2088420863 case "function":
2088520864 return type.flags & TypeFlags.Any ? type : globalFunctionType;
2088620865 case "object":
2088720866 return type.flags & TypeFlags.Unknown ? getUnionType([nonPrimitiveType, nullType]) : type;
2088820867 default:
20889- return typeofTypesByName.get(text) || type ;
20868+ return typeofTypesByName.get(text);
2089020869 }
2089120870 }
2089220871
20893- function narrowTypeForTypeofSwitch(candidate: Type) {
20872+ // When narrowing a union type by a `typeof` guard using type-facts alone, constituent types that are
20873+ // super-types of the implied guard will be retained in the final type: this is because type-facts only
20874+ // filter. Instead, we would like to replace those union constituents with the more precise type implied by
20875+ // the guard. For example: narrowing `{} | undefined` by `"boolean"` should produce the type `boolean`, not
20876+ // the filtered type `{}`. For this reason we narrow constituents of the union individually, in addition to
20877+ // filtering by type-facts.
20878+ function narrowUnionMemberByTypeof(candidate: Type) {
2089420879 return (type: Type) => {
20880+ if (isTypeSubtypeOf(type, candidate)) {
20881+ return type;
20882+ }
2089520883 if (isTypeSubtypeOf(candidate, type)) {
2089620884 return candidate;
2089720885 }
@@ -20916,11 +20904,9 @@ namespace ts {
2091620904 let clauseWitnesses: string[];
2091720905 let switchFacts: TypeFacts;
2091820906 if (defaultCaseLocation > -1) {
20919- // We no longer need the undefined denoting an
20920- // explicit default case. Remove the undefined and
20921- // fix-up clauseStart and clauseEnd. This means
20922- // that we don't have to worry about undefined
20923- // in the witness array.
20907+ // We no longer need the undefined denoting an explicit default case. Remove the undefined and
20908+ // fix-up clauseStart and clauseEnd. This means that we don't have to worry about undefined in the
20909+ // witness array.
2092420910 const witnesses = <string[]>switchWitnesses.filter(witness => witness !== undefined);
2092520911 // The adjusted clause start and end after removing the `default` statement.
2092620912 const fixedClauseStart = defaultCaseLocation < clauseStart ? clauseStart - 1 : clauseStart;
@@ -20963,11 +20949,8 @@ namespace ts {
2096320949 boolean. We know that number cannot be selected
2096420950 because it is caught in the first clause.
2096520951 */
20966- let impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofCase(type, text))), switchFacts);
20967- if (impliedType.flags & TypeFlags.Union) {
20968- impliedType = getAssignmentReducedType(impliedType as UnionType, getBaseConstraintOrType(type));
20969- }
20970- return getTypeWithFacts(mapType(type, narrowTypeForTypeofSwitch(impliedType)), switchFacts);
20952+ const impliedType = getTypeWithFacts(getUnionType(clauseWitnesses.map(text => getImpliedTypeFromTypeofGuard(type, text) || type)), switchFacts);
20953+ return getTypeWithFacts(mapType(type, narrowUnionMemberByTypeof(impliedType)), switchFacts);
2097120954 }
2097220955
2097320956 function isMatchingConstructorReference(expr: Expression) {
0 commit comments