From dc415c5c5e81d6578f58cb1847d0c27926e8a005 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 25 Jul 2019 09:56:36 -0700
Subject: [PATCH 01/10] Infer between closely matching types in unions and
 intersections

---
 src/compiler/checker.ts | 114 +++++++++++++++++++---------------------
 1 file changed, 55 insertions(+), 59 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index f56f56223cf5a..54c8d05e50249 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -15497,37 +15497,27 @@ namespace ts {
                         }
                         return;
                     }
-                    // Find each source constituent type that has an identically matching target constituent
-                    // type, and for each such type infer from the type to itself. When inferring from a
-                    // type to itself we effectively find all type parameter occurrences within that type
-                    // and infer themselves as their type arguments. We have special handling for numeric
-                    // and string literals because the number and string types are not represented as unions
-                    // of all their possible values.
-                    let matchingTypes: Type[] | undefined;
-                    for (const t of (<UnionOrIntersectionType>source).types) {
-                        const matched = findMatchedType(t, <UnionOrIntersectionType>target);
-                        if (matched) {
-                            (matchingTypes || (matchingTypes = [])).push(matched);
-                            inferFromTypes(matched, matched);
-                        }
-                    }
-                    // Next, to improve the quality of inferences, reduce the source and target types by
-                    // removing the identically matched constituents. For example, when inferring from
-                    // 'string | string[]' to 'string | T' we reduce the types to 'string[]' and 'T'.
-                    if (matchingTypes) {
-                        const s = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>source, matchingTypes);
-                        const t = removeTypesFromUnionOrIntersection(<UnionOrIntersectionType>target, matchingTypes);
-                        if (!(s && t)) return;
-                        source = s;
-                        target = t;
+                    // First, infer between exactly matching source and target constituents and remove
+                    // the matching types. Types exactly match when they are identical or, in union
+                    // types, when the source is a literal and the target is the corresponding primitive.
+                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseExactlyMatchedBy : isTypeExactlyMatchedBy;
+                    const [tempSources, tempTargets] = inferFromMatchingTypes((<UnionOrIntersectionType>source).types, (<UnionOrIntersectionType>target).types, matching);
+                    // Next, infer between closely matching source and target constituents and remove
+                    // the matching types. Types closely match when they are instantiations of the same
+                    // object type or instantiations of the same type alias.
+                    const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy);
+                    if (sources.length === 0 || targets.length === 0) {
+                        return;
                     }
+                    source = source.flags & TypeFlags.Union ? getUnionType(sources) : getIntersectionType(sources);
+                    target = target.flags & TypeFlags.Union ? getUnionType(targets) : getIntersectionType(targets);
                 }
                 else if (target.flags & TypeFlags.Union && !(target.flags & TypeFlags.EnumLiteral) || target.flags & TypeFlags.Intersection) {
-                    const matched = findMatchedType(source, <UnionOrIntersectionType>target);
-                    if (matched) {
-                        inferFromTypes(matched, matched);
-                        return;
-                    }
+                    // This block of code is an optimized version of the block above for the simpler case
+                    // of a singleton source type.
+                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseExactlyMatchedBy : isTypeExactlyMatchedBy;
+                    if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, matching)) return;
+                    if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, isTypeCloselyMatchedBy)) return;
                 }
                 else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
                     target = getActualTypeVariable(target);
@@ -15675,6 +15665,35 @@ namespace ts {
                 visited.set(key, inferenceCount - startCount);
             }
 
+            function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
+                let matched = false;
+                for (const t of targets) {
+                    if (matches(source, t)) {
+                        inferFromTypes(source, t);
+                        matched = true;
+                    }
+                }
+                return matched;
+            }
+
+            function inferFromMatchingTypes(sources: Type[], targets: Type[], matches: (s: Type, t: Type) => boolean): [Type[], Type[]] {
+                let matchedSources: Type[] | undefined;
+                let matchedTargets: Type[] | undefined;
+                for (const t of targets) {
+                    for (const s of sources) {
+                        if (matches(s, t)) {
+                            inferFromTypes(s, t);
+                            matchedSources = appendIfUnique(matchedSources, s);
+                            matchedTargets = appendIfUnique(matchedTargets, t);
+                        }
+                    }
+                }
+                return [
+                    matchedSources ? filter(sources, t => !contains(matchedSources, t)) : sources,
+                    matchedTargets ? filter(targets, t => !contains(matchedTargets, t)) : targets,
+                ];
+            }
+
             function inferFromTypeArguments(sourceTypes: readonly Type[], targetTypes: readonly Type[], variances: readonly VarianceFlags[]) {
                 const count = sourceTypes.length < targetTypes.length ? sourceTypes.length : targetTypes.length;
                 for (let i = 0; i < count; i++) {
@@ -15955,47 +15974,24 @@ namespace ts {
             }
         }
 
-        function isMatchableType(type: Type) {
+        function isNonObjectOrAnonymousType(type: Type) {
             // We exclude non-anonymous object types because some frameworks (e.g. Ember) rely on the ability to
             // infer between types that don't witness their type variables. Such types would otherwise be eliminated
             // because they appear identical.
             return !(type.flags & TypeFlags.Object) || !!(getObjectFlags(type) & ObjectFlags.Anonymous);
         }
 
-        function typeMatchedBySomeType(type: Type, types: Type[]): boolean {
-            for (const t of types) {
-                if (t === type || isMatchableType(t) && isMatchableType(type) && isTypeIdenticalTo(t, type)) {
-                    return true;
-                }
-            }
-            return false;
+        function isTypeExactlyMatchedBy(s: Type, t: Type) {
+            return s === t || isNonObjectOrAnonymousType(s) && isNonObjectOrAnonymousType(t) && isTypeIdenticalTo(s, t);
         }
 
-        function findMatchedType(type: Type, target: UnionOrIntersectionType) {
-            if (typeMatchedBySomeType(type, target.types)) {
-                return type;
-            }
-            if (type.flags & (TypeFlags.NumberLiteral | TypeFlags.StringLiteral) && target.flags & TypeFlags.Union) {
-                const base = getBaseTypeOfLiteralType(type);
-                if (typeMatchedBySomeType(base, target.types)) {
-                    return base;
-                }
-            }
-            return undefined;
+        function isTypeOrBaseExactlyMatchedBy(s: Type, t: Type) {
+            return isTypeExactlyMatchedBy(s, t) || !!(s.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) && isTypeIdenticalTo(getBaseTypeOfLiteralType(s), t);
         }
 
-        /**
-         * Return a new union or intersection type computed by removing a given set of types
-         * from a given union or intersection type.
-         */
-        function removeTypesFromUnionOrIntersection(type: UnionOrIntersectionType, typesToRemove: Type[]) {
-            const reducedTypes: Type[] = [];
-            for (const t of type.types) {
-                if (!typeMatchedBySomeType(t, typesToRemove)) {
-                    reducedTypes.push(t);
-                }
-            }
-            return reducedTypes.length ? type.flags & TypeFlags.Union ? getUnionType(reducedTypes) : getIntersectionType(reducedTypes) : undefined;
+        function isTypeCloselyMatchedBy(s: Type, t: Type) {
+            return !!(s.flags & TypeFlags.Object && t.flags & TypeFlags.Object && s.symbol && s.symbol === t.symbol ||
+                s.aliasSymbol && s.aliasTypeArguments && s.aliasSymbol === t.aliasSymbol);
         }
 
         function hasPrimitiveConstraint(type: TypeParameter): boolean {

From 35de142943ea867f302fc8318afa9e9f9bb7c0ad Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 25 Jul 2019 11:50:50 -0700
Subject: [PATCH 02/10] Accept new baselines

---
 ...nalNoInfiniteInstantiationDepth.errors.txt | 26 +++++++------------
 ...ferredInferenceAllowsAssignment.errors.txt | 26 +++++++------------
 2 files changed, 20 insertions(+), 32 deletions(-)

diff --git a/tests/baselines/reference/circularlyConstrainedMappedTypeContainingConditionalNoInfiniteInstantiationDepth.errors.txt b/tests/baselines/reference/circularlyConstrainedMappedTypeContainingConditionalNoInfiniteInstantiationDepth.errors.txt
index c7a323f12836f..4fad6997145ff 100644
--- a/tests/baselines/reference/circularlyConstrainedMappedTypeContainingConditionalNoInfiniteInstantiationDepth.errors.txt
+++ b/tests/baselines/reference/circularlyConstrainedMappedTypeContainingConditionalNoInfiniteInstantiationDepth.errors.txt
@@ -45,14 +45,11 @@ tests/cases/compiler/circularlyConstrainedMappedTypeContainingConditionalNoInfin
                                                                                         Type 'TInjectedProps[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
                                                                                           Type 'TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
                                                                                             Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-                                                                                              Type 'GetProps<C>[string] | (TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string])' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-                                                                                                Type 'GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-                                                                                                  Type 'GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                    Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                      Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                        Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                          Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                            Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                              Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                  Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                    Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                      Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
 
 
 ==== tests/cases/compiler/circularlyConstrainedMappedTypeContainingConditionalNoInfiniteInstantiationDepth.ts (1 errors) ====
@@ -167,12 +164,9 @@ tests/cases/compiler/circularlyConstrainedMappedTypeContainingConditionalNoInfin
 !!! error TS2344:                                                                                         Type 'TInjectedProps[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
 !!! error TS2344:                                                                                           Type 'TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
 !!! error TS2344:                                                                                             Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-!!! error TS2344:                                                                                               Type 'GetProps<C>[string] | (TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string])' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-!!! error TS2344:                                                                                                 Type 'GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-!!! error TS2344:                                                                                                   Type 'GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                     Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                       Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                         Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                           Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                             Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                               Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                 Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                   Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                     Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                       Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
     
\ No newline at end of file
diff --git a/tests/baselines/reference/reactReduxLikeDeferredInferenceAllowsAssignment.errors.txt b/tests/baselines/reference/reactReduxLikeDeferredInferenceAllowsAssignment.errors.txt
index 0556ae25c9160..0acfd4a836572 100644
--- a/tests/baselines/reference/reactReduxLikeDeferredInferenceAllowsAssignment.errors.txt
+++ b/tests/baselines/reference/reactReduxLikeDeferredInferenceAllowsAssignment.errors.txt
@@ -45,14 +45,11 @@ tests/cases/compiler/reactReduxLikeDeferredInferenceAllowsAssignment.ts(76,50):
                                                                                         Type 'TInjectedProps[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
                                                                                           Type 'TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
                                                                                             Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-                                                                                              Type 'GetProps<C>[string] | (TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string])' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-                                                                                                Type 'GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-                                                                                                  Type 'GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                    Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                      Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                        Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                          Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-                                                                                                            Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                              Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                  Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                    Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+                                                                                                      Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
 
 
 ==== tests/cases/compiler/reactReduxLikeDeferredInferenceAllowsAssignment.ts (1 errors) ====
@@ -180,14 +177,11 @@ tests/cases/compiler/reactReduxLikeDeferredInferenceAllowsAssignment.ts(76,50):
 !!! error TS2344:                                                                                         Type 'TInjectedProps[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
 !!! error TS2344:                                                                                           Type 'TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
 !!! error TS2344:                                                                                             Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-!!! error TS2344:                                                                                               Type 'GetProps<C>[string] | (TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string])' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-!!! error TS2344:                                                                                                 Type 'GetProps<C>[string]' is not assignable to type '(TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never) | undefined'.
-!!! error TS2344:                                                                                                   Type 'GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                     Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                       Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                         Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                           Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
-!!! error TS2344:                                                                                                             Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                               Type 'string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & string] extends GetProps<C>[keyof TInjectedProps & string] ? GetProps<C>[keyof TInjectedProps & string] : TInjectedProps[keyof TInjectedProps & string] : GetProps<C>[string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                 Type 'keyof GetProps<C> & string extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] extends GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] ? GetProps<C>[keyof TInjectedProps & keyof GetProps<C> & string] : TInjectedProps[keyof TInjectedProps & keyof GetProps<C> & string] : GetProps<C>[keyof GetProps<C> & string]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                   Type 'Extract<string, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] extends GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] ? GetProps<C>[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : TInjectedProps[keyof TInjectedProps & Extract<string, keyof GetProps<C>>] : GetProps<C>[Extract<string, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                     Type 'Extract<keyof TInjectedProps, keyof GetProps<C>> extends keyof TInjectedProps ? TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] extends GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] ? GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>] : TInjectedProps[Extract<keyof TInjectedProps, keyof GetProps<C>>] : GetProps<C>[Extract<keyof TInjectedProps, keyof GetProps<C>>]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
+!!! error TS2344:                                                                                                       Type 'P extends keyof TInjectedProps ? TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : TInjectedProps[P] : GetProps<C>[P]' is not assignable to type 'TInjectedProps[P] extends GetProps<C>[P] ? GetProps<C>[P] : never'.
     >;
     
     declare const connect: {

From b9d27c0f2ce5df82b8899aca94ed752b626978b0 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 25 Jul 2019 11:52:08 -0700
Subject: [PATCH 03/10] Add regression tests

---
 .../unionAndIntersectionInference3.ts         | 35 +++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts
index aad083a626b26..17057de2b848a 100644
--- a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts
+++ b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts
@@ -1,7 +1,42 @@
 // @strict: true
+// @target: esnext
 
 // Repro from #30720
 
 type Maybe<T> = T | undefined;
 declare function concatMaybe<T>(...args: (Maybe<T> | Maybe<T>[])[]): T[];
 concatMaybe([1, 2, 3], 4);
+
+// Repros from #32247
+
+const g: <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>) => Promise<U> = async <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>): Promise<U> => {
+  throw com;
+};
+
+interface Foo1<T> {
+    test(value: T): void;
+}
+
+interface Bar1<T> {
+    test(value: T | PromiseLike<T>): void;
+}
+
+declare let f1: <T>(x: Foo1<T> | Bar1<T>) => Promise<T>;
+declare let f2: <U>(x: Foo1<U> | Bar1<U>) => Promise<U>;
+
+f1 = f2;
+f2 = f1;
+
+type Foo2<T> = {
+    test(value: T): void;
+}
+
+type Bar2<T> = {
+    test(value: T | PromiseLike<T>): void;
+}
+
+declare let g1: <T>(x: Foo2<T> | Bar2<T>) => Promise<T>;
+declare let g2: <U>(x: Foo2<U> | Bar2<U>) => Promise<U>;
+
+g1 = g2;
+g2 = g1;

From 540134840da0acea39f103b12719392c41b9a67d Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 25 Jul 2019 11:52:15 -0700
Subject: [PATCH 04/10] Accept new baselines

---
 .../unionAndIntersectionInference3.js         |  42 ++++++
 .../unionAndIntersectionInference3.symbols    | 142 ++++++++++++++++++
 .../unionAndIntersectionInference3.types      |  77 ++++++++++
 3 files changed, 261 insertions(+)

diff --git a/tests/baselines/reference/unionAndIntersectionInference3.js b/tests/baselines/reference/unionAndIntersectionInference3.js
index 4cc0c2f667e59..c9f5cffa1f76b 100644
--- a/tests/baselines/reference/unionAndIntersectionInference3.js
+++ b/tests/baselines/reference/unionAndIntersectionInference3.js
@@ -4,9 +4,51 @@
 type Maybe<T> = T | undefined;
 declare function concatMaybe<T>(...args: (Maybe<T> | Maybe<T>[])[]): T[];
 concatMaybe([1, 2, 3], 4);
+
+// Repros from #32247
+
+const g: <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>) => Promise<U> = async <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>): Promise<U> => {
+  throw com;
+};
+
+interface Foo1<T> {
+    test(value: T): void;
+}
+
+interface Bar1<T> {
+    test(value: T | PromiseLike<T>): void;
+}
+
+declare let f1: <T>(x: Foo1<T> | Bar1<T>) => Promise<T>;
+declare let f2: <U>(x: Foo1<U> | Bar1<U>) => Promise<U>;
+
+f1 = f2;
+f2 = f1;
+
+type Foo2<T> = {
+    test(value: T): void;
+}
+
+type Bar2<T> = {
+    test(value: T | PromiseLike<T>): void;
+}
+
+declare let g1: <T>(x: Foo2<T> | Bar2<T>) => Promise<T>;
+declare let g2: <U>(x: Foo2<U> | Bar2<U>) => Promise<U>;
+
+g1 = g2;
+g2 = g1;
 
 
 //// [unionAndIntersectionInference3.js]
 "use strict";
 // Repro from #30720
 concatMaybe([1, 2, 3], 4);
+// Repros from #32247
+const g = async (com) => {
+    throw com;
+};
+f1 = f2;
+f2 = f1;
+g1 = g2;
+g2 = g1;
diff --git a/tests/baselines/reference/unionAndIntersectionInference3.symbols b/tests/baselines/reference/unionAndIntersectionInference3.symbols
index be24bc0cc7781..b854ee1ce42b1 100644
--- a/tests/baselines/reference/unionAndIntersectionInference3.symbols
+++ b/tests/baselines/reference/unionAndIntersectionInference3.symbols
@@ -19,3 +19,145 @@ declare function concatMaybe<T>(...args: (Maybe<T> | Maybe<T>[])[]): T[];
 concatMaybe([1, 2, 3], 4);
 >concatMaybe : Symbol(concatMaybe, Decl(unionAndIntersectionInference3.ts, 2, 30))
 
+// Repros from #32247
+
+const g: <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>) => Promise<U> = async <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>): Promise<U> => {
+>g : Symbol(g, Decl(unionAndIntersectionInference3.ts, 8, 5))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 10))
+>R : Symbol(R, Decl(unionAndIntersectionInference3.ts, 8, 12))
+>S : Symbol(S, Decl(unionAndIntersectionInference3.ts, 8, 15))
+>com : Symbol(com, Decl(unionAndIntersectionInference3.ts, 8, 19))
+>Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --))
+>S : Symbol(S, Decl(unionAndIntersectionInference3.ts, 8, 15))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 10))
+>R : Symbol(R, Decl(unionAndIntersectionInference3.ts, 8, 12))
+>AsyncIterator : Symbol(AsyncIterator, Decl(lib.es2018.asynciterable.d.ts, --, --))
+>S : Symbol(S, Decl(unionAndIntersectionInference3.ts, 8, 15))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 10))
+>R : Symbol(R, Decl(unionAndIntersectionInference3.ts, 8, 12))
+>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 10))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 97))
+>R : Symbol(R, Decl(unionAndIntersectionInference3.ts, 8, 99))
+>S : Symbol(S, Decl(unionAndIntersectionInference3.ts, 8, 102))
+>com : Symbol(com, Decl(unionAndIntersectionInference3.ts, 8, 106))
+>Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --))
+>S : Symbol(S, Decl(unionAndIntersectionInference3.ts, 8, 102))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 97))
+>R : Symbol(R, Decl(unionAndIntersectionInference3.ts, 8, 99))
+>AsyncIterator : Symbol(AsyncIterator, Decl(lib.es2018.asynciterable.d.ts, --, --))
+>S : Symbol(S, Decl(unionAndIntersectionInference3.ts, 8, 102))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 97))
+>R : Symbol(R, Decl(unionAndIntersectionInference3.ts, 8, 99))
+>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 8, 97))
+
+  throw com;
+>com : Symbol(com, Decl(unionAndIntersectionInference3.ts, 8, 106))
+
+};
+
+interface Foo1<T> {
+>Foo1 : Symbol(Foo1, Decl(unionAndIntersectionInference3.ts, 10, 2))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 12, 15))
+
+    test(value: T): void;
+>test : Symbol(Foo1.test, Decl(unionAndIntersectionInference3.ts, 12, 19))
+>value : Symbol(value, Decl(unionAndIntersectionInference3.ts, 13, 9))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 12, 15))
+}
+
+interface Bar1<T> {
+>Bar1 : Symbol(Bar1, Decl(unionAndIntersectionInference3.ts, 14, 1))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 16, 15))
+
+    test(value: T | PromiseLike<T>): void;
+>test : Symbol(Bar1.test, Decl(unionAndIntersectionInference3.ts, 16, 19))
+>value : Symbol(value, Decl(unionAndIntersectionInference3.ts, 17, 9))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 16, 15))
+>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 16, 15))
+}
+
+declare let f1: <T>(x: Foo1<T> | Bar1<T>) => Promise<T>;
+>f1 : Symbol(f1, Decl(unionAndIntersectionInference3.ts, 20, 11))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 20, 17))
+>x : Symbol(x, Decl(unionAndIntersectionInference3.ts, 20, 20))
+>Foo1 : Symbol(Foo1, Decl(unionAndIntersectionInference3.ts, 10, 2))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 20, 17))
+>Bar1 : Symbol(Bar1, Decl(unionAndIntersectionInference3.ts, 14, 1))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 20, 17))
+>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 20, 17))
+
+declare let f2: <U>(x: Foo1<U> | Bar1<U>) => Promise<U>;
+>f2 : Symbol(f2, Decl(unionAndIntersectionInference3.ts, 21, 11))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 21, 17))
+>x : Symbol(x, Decl(unionAndIntersectionInference3.ts, 21, 20))
+>Foo1 : Symbol(Foo1, Decl(unionAndIntersectionInference3.ts, 10, 2))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 21, 17))
+>Bar1 : Symbol(Bar1, Decl(unionAndIntersectionInference3.ts, 14, 1))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 21, 17))
+>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 21, 17))
+
+f1 = f2;
+>f1 : Symbol(f1, Decl(unionAndIntersectionInference3.ts, 20, 11))
+>f2 : Symbol(f2, Decl(unionAndIntersectionInference3.ts, 21, 11))
+
+f2 = f1;
+>f2 : Symbol(f2, Decl(unionAndIntersectionInference3.ts, 21, 11))
+>f1 : Symbol(f1, Decl(unionAndIntersectionInference3.ts, 20, 11))
+
+type Foo2<T> = {
+>Foo2 : Symbol(Foo2, Decl(unionAndIntersectionInference3.ts, 24, 8))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 26, 10))
+
+    test(value: T): void;
+>test : Symbol(test, Decl(unionAndIntersectionInference3.ts, 26, 16))
+>value : Symbol(value, Decl(unionAndIntersectionInference3.ts, 27, 9))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 26, 10))
+}
+
+type Bar2<T> = {
+>Bar2 : Symbol(Bar2, Decl(unionAndIntersectionInference3.ts, 28, 1))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 30, 10))
+
+    test(value: T | PromiseLike<T>): void;
+>test : Symbol(test, Decl(unionAndIntersectionInference3.ts, 30, 16))
+>value : Symbol(value, Decl(unionAndIntersectionInference3.ts, 31, 9))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 30, 10))
+>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 30, 10))
+}
+
+declare let g1: <T>(x: Foo2<T> | Bar2<T>) => Promise<T>;
+>g1 : Symbol(g1, Decl(unionAndIntersectionInference3.ts, 34, 11))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 34, 17))
+>x : Symbol(x, Decl(unionAndIntersectionInference3.ts, 34, 20))
+>Foo2 : Symbol(Foo2, Decl(unionAndIntersectionInference3.ts, 24, 8))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 34, 17))
+>Bar2 : Symbol(Bar2, Decl(unionAndIntersectionInference3.ts, 28, 1))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 34, 17))
+>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 34, 17))
+
+declare let g2: <U>(x: Foo2<U> | Bar2<U>) => Promise<U>;
+>g2 : Symbol(g2, Decl(unionAndIntersectionInference3.ts, 35, 11))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 35, 17))
+>x : Symbol(x, Decl(unionAndIntersectionInference3.ts, 35, 20))
+>Foo2 : Symbol(Foo2, Decl(unionAndIntersectionInference3.ts, 24, 8))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 35, 17))
+>Bar2 : Symbol(Bar2, Decl(unionAndIntersectionInference3.ts, 28, 1))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 35, 17))
+>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2018.promise.d.ts, --, --))
+>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 35, 17))
+
+g1 = g2;
+>g1 : Symbol(g1, Decl(unionAndIntersectionInference3.ts, 34, 11))
+>g2 : Symbol(g2, Decl(unionAndIntersectionInference3.ts, 35, 11))
+
+g2 = g1;
+>g2 : Symbol(g2, Decl(unionAndIntersectionInference3.ts, 35, 11))
+>g1 : Symbol(g1, Decl(unionAndIntersectionInference3.ts, 34, 11))
+
diff --git a/tests/baselines/reference/unionAndIntersectionInference3.types b/tests/baselines/reference/unionAndIntersectionInference3.types
index 8497cb1e5161f..acbca0a1eae51 100644
--- a/tests/baselines/reference/unionAndIntersectionInference3.types
+++ b/tests/baselines/reference/unionAndIntersectionInference3.types
@@ -17,3 +17,80 @@ concatMaybe([1, 2, 3], 4);
 >3 : 3
 >4 : 4
 
+// Repros from #32247
+
+const g: <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>) => Promise<U> = async <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>): Promise<U> => {
+>g : <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>) => Promise<U>
+>com : () => Iterator<S, U, R> | AsyncIterator<S, U, R>
+>async <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>): Promise<U> => {  throw com;} : <U, R, S>(com: () => Iterator<S, U, R> | AsyncIterator<S, U, R>) => Promise<U>
+>com : () => Iterator<S, U, R> | AsyncIterator<S, U, R>
+
+  throw com;
+>com : () => Iterator<S, U, R> | AsyncIterator<S, U, R>
+
+};
+
+interface Foo1<T> {
+    test(value: T): void;
+>test : (value: T) => void
+>value : T
+}
+
+interface Bar1<T> {
+    test(value: T | PromiseLike<T>): void;
+>test : (value: T | PromiseLike<T>) => void
+>value : T | PromiseLike<T>
+}
+
+declare let f1: <T>(x: Foo1<T> | Bar1<T>) => Promise<T>;
+>f1 : <T>(x: Foo1<T> | Bar1<T>) => Promise<T>
+>x : Foo1<T> | Bar1<T>
+
+declare let f2: <U>(x: Foo1<U> | Bar1<U>) => Promise<U>;
+>f2 : <U>(x: Foo1<U> | Bar1<U>) => Promise<U>
+>x : Foo1<U> | Bar1<U>
+
+f1 = f2;
+>f1 = f2 : <U>(x: Foo1<U> | Bar1<U>) => Promise<U>
+>f1 : <T>(x: Foo1<T> | Bar1<T>) => Promise<T>
+>f2 : <U>(x: Foo1<U> | Bar1<U>) => Promise<U>
+
+f2 = f1;
+>f2 = f1 : <T>(x: Foo1<T> | Bar1<T>) => Promise<T>
+>f2 : <U>(x: Foo1<U> | Bar1<U>) => Promise<U>
+>f1 : <T>(x: Foo1<T> | Bar1<T>) => Promise<T>
+
+type Foo2<T> = {
+>Foo2 : Foo2<T>
+
+    test(value: T): void;
+>test : (value: T) => void
+>value : T
+}
+
+type Bar2<T> = {
+>Bar2 : Bar2<T>
+
+    test(value: T | PromiseLike<T>): void;
+>test : (value: T | PromiseLike<T>) => void
+>value : T | PromiseLike<T>
+}
+
+declare let g1: <T>(x: Foo2<T> | Bar2<T>) => Promise<T>;
+>g1 : <T>(x: Foo2<T> | Bar2<T>) => Promise<T>
+>x : Foo2<T> | Bar2<T>
+
+declare let g2: <U>(x: Foo2<U> | Bar2<U>) => Promise<U>;
+>g2 : <U>(x: Foo2<U> | Bar2<U>) => Promise<U>
+>x : Foo2<U> | Bar2<U>
+
+g1 = g2;
+>g1 = g2 : <U>(x: Foo2<U> | Bar2<U>) => Promise<U>
+>g1 : <T>(x: Foo2<T> | Bar2<T>) => Promise<T>
+>g2 : <U>(x: Foo2<U> | Bar2<U>) => Promise<U>
+
+g2 = g1;
+>g2 = g1 : <T>(x: Foo2<T> | Bar2<T>) => Promise<T>
+>g2 : <U>(x: Foo2<U> | Bar2<U>) => Promise<U>
+>g1 : <T>(x: Foo2<T> | Bar2<T>) => Promise<T>
+

From 4c76bae8880d97d01847064cd98befba8f42028a Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Thu, 25 Jul 2019 14:03:17 -0700
Subject: [PATCH 05/10] Don't exclude non-anonymous object types in identity
 checks

---
 src/compiler/checker.ts | 19 ++++---------------
 1 file changed, 4 insertions(+), 15 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 54c8d05e50249..0f8326c1fac4e 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -15500,7 +15500,7 @@ namespace ts {
                     // First, infer between exactly matching source and target constituents and remove
                     // the matching types. Types exactly match when they are identical or, in union
                     // types, when the source is a literal and the target is the corresponding primitive.
-                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseExactlyMatchedBy : isTypeExactlyMatchedBy;
+                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseIdenticalTo : isTypeIdenticalTo;
                     const [tempSources, tempTargets] = inferFromMatchingTypes((<UnionOrIntersectionType>source).types, (<UnionOrIntersectionType>target).types, matching);
                     // Next, infer between closely matching source and target constituents and remove
                     // the matching types. Types closely match when they are instantiations of the same
@@ -15515,7 +15515,7 @@ namespace ts {
                 else if (target.flags & TypeFlags.Union && !(target.flags & TypeFlags.EnumLiteral) || target.flags & TypeFlags.Intersection) {
                     // This block of code is an optimized version of the block above for the simpler case
                     // of a singleton source type.
-                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseExactlyMatchedBy : isTypeExactlyMatchedBy;
+                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseIdenticalTo : isTypeIdenticalTo;
                     if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, matching)) return;
                     if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, isTypeCloselyMatchedBy)) return;
                 }
@@ -15974,19 +15974,8 @@ namespace ts {
             }
         }
 
-        function isNonObjectOrAnonymousType(type: Type) {
-            // We exclude non-anonymous object types because some frameworks (e.g. Ember) rely on the ability to
-            // infer between types that don't witness their type variables. Such types would otherwise be eliminated
-            // because they appear identical.
-            return !(type.flags & TypeFlags.Object) || !!(getObjectFlags(type) & ObjectFlags.Anonymous);
-        }
-
-        function isTypeExactlyMatchedBy(s: Type, t: Type) {
-            return s === t || isNonObjectOrAnonymousType(s) && isNonObjectOrAnonymousType(t) && isTypeIdenticalTo(s, t);
-        }
-
-        function isTypeOrBaseExactlyMatchedBy(s: Type, t: Type) {
-            return isTypeExactlyMatchedBy(s, t) || !!(s.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) && isTypeIdenticalTo(getBaseTypeOfLiteralType(s), t);
+        function isTypeOrBaseIdenticalTo(s: Type, t: Type) {
+            return isTypeIdenticalTo(s, t) || !!(s.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral)) && isTypeIdenticalTo(getBaseTypeOfLiteralType(s), t);
         }
 
         function isTypeCloselyMatchedBy(s: Type, t: Type) {

From 00f41e5693e55ede6a243b8fe0f4d982081ef2dd Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Fri, 26 Jul 2019 11:03:31 -0700
Subject: [PATCH 06/10] Less aggressive reduction of intersection types

---
 src/compiler/checker.ts | 66 ++++++++++++++++++++++++-----------------
 1 file changed, 38 insertions(+), 28 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 0f8326c1fac4e..8ef7b3453a249 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -15310,7 +15310,7 @@ namespace ts {
                 objectFlags & ObjectFlags.Reference && forEach((<TypeReference>type).typeArguments, couldContainTypeVariables) ||
                 objectFlags & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & (SymbolFlags.Function | SymbolFlags.Method | SymbolFlags.Class | SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral) && type.symbol.declarations ||
                 objectFlags & ObjectFlags.Mapped ||
-                type.flags & TypeFlags.UnionOrIntersection && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
+                type.flags & TypeFlags.UnionOrIntersection && !(type.flags & TypeFlags.EnumLiteral) && couldUnionOrIntersectionContainTypeVariables(<UnionOrIntersectionType>type));
         }
 
         function couldUnionOrIntersectionContainTypeVariables(type: UnionOrIntersectionType): boolean {
@@ -15487,37 +15487,47 @@ namespace ts {
                     inferFromTypeArguments(source.aliasTypeArguments, target.aliasTypeArguments!, getAliasVariances(source.aliasSymbol));
                     return;
                 }
-                if (source.flags & TypeFlags.Union && target.flags & TypeFlags.Union && !(source.flags & TypeFlags.EnumLiteral && target.flags & TypeFlags.EnumLiteral) ||
-                    source.flags & TypeFlags.Intersection && target.flags & TypeFlags.Intersection) {
-                    // Source and target are both unions or both intersections. If source and target
-                    // are the same type, just relate each constituent type to itself.
-                    if (source === target) {
-                        for (const t of (<UnionOrIntersectionType>source).types) {
-                            inferFromTypes(t, t);
+                if (source === target && source.flags & TypeFlags.UnionOrIntersection) {
+                    // When source and target are the same union or intersection type, just relate each constituent
+                    // type to itself.
+                    for (const t of (<UnionOrIntersectionType>source).types) {
+                        inferFromTypes(t, t);
+                    }
+                    return;
+                }
+                if (target.flags & TypeFlags.Union) {
+                    if (source.flags & TypeFlags.Union) {
+                        // First, infer between identically matching source and target constituents and remove the
+                        // matching types.
+                        const [tempSources, tempTargets] = inferFromMatchingTypes((<UnionType>source).types, (<UnionType>target).types, isTypeOrBaseIdenticalTo);
+                        // Next, infer between closely matching source and target constituents and remove
+                        // the matching types. Types closely match when they are instantiations of the same
+                        // object type or instantiations of the same type alias.
+                        const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy);
+                        if (sources.length === 0 || targets.length === 0) {
+                            return;
                         }
-                        return;
+                        source = getUnionType(sources);
+                        target = getUnionType(targets);
                     }
-                    // First, infer between exactly matching source and target constituents and remove
-                    // the matching types. Types exactly match when they are identical or, in union
-                    // types, when the source is a literal and the target is the corresponding primitive.
-                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseIdenticalTo : isTypeIdenticalTo;
-                    const [tempSources, tempTargets] = inferFromMatchingTypes((<UnionOrIntersectionType>source).types, (<UnionOrIntersectionType>target).types, matching);
-                    // Next, infer between closely matching source and target constituents and remove
-                    // the matching types. Types closely match when they are instantiations of the same
-                    // object type or instantiations of the same type alias.
-                    const [sources, targets] = inferFromMatchingTypes(tempSources, tempTargets, isTypeCloselyMatchedBy);
-                    if (sources.length === 0 || targets.length === 0) {
-                        return;
+                    else {
+                        if (inferFromMatchingType(source, (<UnionType>target).types, isTypeOrBaseIdenticalTo)) return;
+                        if (inferFromMatchingType(source, (<UnionType>target).types, isTypeCloselyMatchedBy)) return;
                     }
-                    source = source.flags & TypeFlags.Union ? getUnionType(sources) : getIntersectionType(sources);
-                    target = target.flags & TypeFlags.Union ? getUnionType(targets) : getIntersectionType(targets);
                 }
-                else if (target.flags & TypeFlags.Union && !(target.flags & TypeFlags.EnumLiteral) || target.flags & TypeFlags.Intersection) {
-                    // This block of code is an optimized version of the block above for the simpler case
-                    // of a singleton source type.
-                    const matching = target.flags & TypeFlags.Union ? isTypeOrBaseIdenticalTo : isTypeIdenticalTo;
-                    if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, matching)) return;
-                    if (inferFromMatchingType(source, (<UnionOrIntersectionType>target).types, isTypeCloselyMatchedBy)) return;
+                else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types, t => !!getInferenceInfoForType(t))) {
+                    if (source.flags & TypeFlags.Intersection) {
+                        // Infer between identically matching source and target constituents and remove the matching types.
+                        const [sources, targets] = inferFromMatchingTypes((<IntersectionType>source).types, (<IntersectionType>target).types, isTypeIdenticalTo);
+                        if (sources.length === 0 || targets.length === 0) {
+                            return;
+                        }
+                        source = getIntersectionType(sources);
+                        target = getIntersectionType(targets);
+                    }
+                    else if (!(source.flags & TypeFlags.Union)) {
+                        if (inferFromMatchingType(source, (<IntersectionType>target).types, isTypeIdenticalTo)) return;
+                    }
                 }
                 else if (target.flags & (TypeFlags.IndexedAccess | TypeFlags.Substitution)) {
                     target = getActualTypeVariable(target);

From bb87332e73a1024dd3aa1b0fef18575cb504057d Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Fri, 26 Jul 2019 13:12:44 -0700
Subject: [PATCH 07/10] Add more comments

---
 src/compiler/checker.ts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 8ef7b3453a249..a69751673f67c 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -15516,6 +15516,10 @@ namespace ts {
                     }
                 }
                 else if (target.flags & TypeFlags.Intersection && some((<IntersectionType>target).types, t => !!getInferenceInfoForType(t))) {
+                    // We reduce intersection types only when they contain naked type parameters. For example, when
+                    // inferring from 'string[] & { extra: any }' to 'string[] & T' we want to remove string[] and
+                    // infer { extra: any } for T. But when inferring to 'string[] & Iterable<T>' we want to keep the
+                    // string[] on the source side and infer string for T.
                     if (source.flags & TypeFlags.Intersection) {
                         // Infer between identically matching source and target constituents and remove the matching types.
                         const [sources, targets] = inferFromMatchingTypes((<IntersectionType>source).types, (<IntersectionType>target).types, isTypeIdenticalTo);

From ec38799e2a1a9df776882e20d89604c85ec3015f Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Fri, 26 Jul 2019 13:17:00 -0700
Subject: [PATCH 08/10] Add more tests

---
 .../unionAndIntersectionInference3.ts              | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts
index 17057de2b848a..d64b19736952f 100644
--- a/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts
+++ b/tests/cases/conformance/types/typeRelationships/typeInference/unionAndIntersectionInference3.ts
@@ -40,3 +40,17 @@ declare let g2: <U>(x: Foo2<U> | Bar2<U>) => Promise<U>;
 
 g1 = g2;
 g2 = g1;
+
+// Repro from #32572
+
+declare function foo1<T>(obj: string[] & Iterable<T>): T;
+declare function foo2<T>(obj: string[] & T): T;
+
+declare let sa: string[];
+declare let sx: string[] & { extra: number };
+
+let x1 = foo1(sa);  // string
+let y1 = foo1(sx);  // string
+
+let x2 = foo2(sa);  // unknown
+let y2 = foo2(sx);  // { extra: number }

From 1ea4008120a20c4cca416af2096c5f3a46da7063 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Fri, 26 Jul 2019 13:17:06 -0700
Subject: [PATCH 09/10] Accept new baselines

---
 .../unionAndIntersectionInference3.js         | 18 ++++++++
 .../unionAndIntersectionInference3.symbols    | 44 +++++++++++++++++++
 .../unionAndIntersectionInference3.types      | 41 +++++++++++++++++
 3 files changed, 103 insertions(+)

diff --git a/tests/baselines/reference/unionAndIntersectionInference3.js b/tests/baselines/reference/unionAndIntersectionInference3.js
index c9f5cffa1f76b..415d98220a576 100644
--- a/tests/baselines/reference/unionAndIntersectionInference3.js
+++ b/tests/baselines/reference/unionAndIntersectionInference3.js
@@ -38,6 +38,20 @@ declare let g2: <U>(x: Foo2<U> | Bar2<U>) => Promise<U>;
 
 g1 = g2;
 g2 = g1;
+
+// Repro from #32572
+
+declare function foo1<T>(obj: string[] & Iterable<T>): T;
+declare function foo2<T>(obj: string[] & T): T;
+
+declare let sa: string[];
+declare let sx: string[] & { extra: number };
+
+let x1 = foo1(sa);  // string
+let y1 = foo1(sx);  // string
+
+let x2 = foo2(sa);  // unknown
+let y2 = foo2(sx);  // { extra: number }
 
 
 //// [unionAndIntersectionInference3.js]
@@ -52,3 +66,7 @@ f1 = f2;
 f2 = f1;
 g1 = g2;
 g2 = g1;
+let x1 = foo1(sa); // string
+let y1 = foo1(sx); // string
+let x2 = foo2(sa); // unknown
+let y2 = foo2(sx); // { extra: number }
diff --git a/tests/baselines/reference/unionAndIntersectionInference3.symbols b/tests/baselines/reference/unionAndIntersectionInference3.symbols
index b854ee1ce42b1..1d237951b4fd0 100644
--- a/tests/baselines/reference/unionAndIntersectionInference3.symbols
+++ b/tests/baselines/reference/unionAndIntersectionInference3.symbols
@@ -161,3 +161,47 @@ g2 = g1;
 >g2 : Symbol(g2, Decl(unionAndIntersectionInference3.ts, 35, 11))
 >g1 : Symbol(g1, Decl(unionAndIntersectionInference3.ts, 34, 11))
 
+// Repro from #32572
+
+declare function foo1<T>(obj: string[] & Iterable<T>): T;
+>foo1 : Symbol(foo1, Decl(unionAndIntersectionInference3.ts, 38, 8))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 42, 22))
+>obj : Symbol(obj, Decl(unionAndIntersectionInference3.ts, 42, 25))
+>Iterable : Symbol(Iterable, Decl(lib.es2015.iterable.d.ts, --, --))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 42, 22))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 42, 22))
+
+declare function foo2<T>(obj: string[] & T): T;
+>foo2 : Symbol(foo2, Decl(unionAndIntersectionInference3.ts, 42, 57))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 43, 22))
+>obj : Symbol(obj, Decl(unionAndIntersectionInference3.ts, 43, 25))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 43, 22))
+>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 43, 22))
+
+declare let sa: string[];
+>sa : Symbol(sa, Decl(unionAndIntersectionInference3.ts, 45, 11))
+
+declare let sx: string[] & { extra: number };
+>sx : Symbol(sx, Decl(unionAndIntersectionInference3.ts, 46, 11))
+>extra : Symbol(extra, Decl(unionAndIntersectionInference3.ts, 46, 28))
+
+let x1 = foo1(sa);  // string
+>x1 : Symbol(x1, Decl(unionAndIntersectionInference3.ts, 48, 3))
+>foo1 : Symbol(foo1, Decl(unionAndIntersectionInference3.ts, 38, 8))
+>sa : Symbol(sa, Decl(unionAndIntersectionInference3.ts, 45, 11))
+
+let y1 = foo1(sx);  // string
+>y1 : Symbol(y1, Decl(unionAndIntersectionInference3.ts, 49, 3))
+>foo1 : Symbol(foo1, Decl(unionAndIntersectionInference3.ts, 38, 8))
+>sx : Symbol(sx, Decl(unionAndIntersectionInference3.ts, 46, 11))
+
+let x2 = foo2(sa);  // unknown
+>x2 : Symbol(x2, Decl(unionAndIntersectionInference3.ts, 51, 3))
+>foo2 : Symbol(foo2, Decl(unionAndIntersectionInference3.ts, 42, 57))
+>sa : Symbol(sa, Decl(unionAndIntersectionInference3.ts, 45, 11))
+
+let y2 = foo2(sx);  // { extra: number }
+>y2 : Symbol(y2, Decl(unionAndIntersectionInference3.ts, 52, 3))
+>foo2 : Symbol(foo2, Decl(unionAndIntersectionInference3.ts, 42, 57))
+>sx : Symbol(sx, Decl(unionAndIntersectionInference3.ts, 46, 11))
+
diff --git a/tests/baselines/reference/unionAndIntersectionInference3.types b/tests/baselines/reference/unionAndIntersectionInference3.types
index acbca0a1eae51..fb5074743219a 100644
--- a/tests/baselines/reference/unionAndIntersectionInference3.types
+++ b/tests/baselines/reference/unionAndIntersectionInference3.types
@@ -94,3 +94,44 @@ g2 = g1;
 >g2 : <U>(x: Foo2<U> | Bar2<U>) => Promise<U>
 >g1 : <T>(x: Foo2<T> | Bar2<T>) => Promise<T>
 
+// Repro from #32572
+
+declare function foo1<T>(obj: string[] & Iterable<T>): T;
+>foo1 : <T>(obj: string[] & Iterable<T>) => T
+>obj : string[] & Iterable<T>
+
+declare function foo2<T>(obj: string[] & T): T;
+>foo2 : <T>(obj: string[] & T) => T
+>obj : string[] & T
+
+declare let sa: string[];
+>sa : string[]
+
+declare let sx: string[] & { extra: number };
+>sx : string[] & { extra: number; }
+>extra : number
+
+let x1 = foo1(sa);  // string
+>x1 : string
+>foo1(sa) : string
+>foo1 : <T>(obj: string[] & Iterable<T>) => T
+>sa : string[]
+
+let y1 = foo1(sx);  // string
+>y1 : string
+>foo1(sx) : string
+>foo1 : <T>(obj: string[] & Iterable<T>) => T
+>sx : string[] & { extra: number; }
+
+let x2 = foo2(sa);  // unknown
+>x2 : unknown
+>foo2(sa) : unknown
+>foo2 : <T>(obj: string[] & T) => T
+>sa : string[]
+
+let y2 = foo2(sx);  // { extra: number }
+>y2 : { extra: number; }
+>foo2(sx) : { extra: number; }
+>foo2 : <T>(obj: string[] & T) => T
+>sx : string[] & { extra: number; }
+

From a9e0a7766e30eff61f76ca85b380e65f45c8eb12 Mon Sep 17 00:00:00 2001
From: Anders Hejlsberg <andersh@microsoft.com>
Date: Sat, 27 Jul 2019 08:50:26 -0700
Subject: [PATCH 10/10] Record full inference status in visitation cache

---
 src/compiler/checker.ts | 26 +++++++++++++++++---------
 1 file changed, 17 insertions(+), 9 deletions(-)

diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index a69751673f67c..27c8a027cbcba 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -15462,7 +15462,7 @@ namespace ts {
             let visited: Map<number>;
             let bivariant = false;
             let propagationType: Type;
-            let inferenceCount = 0;
+            let inferenceMatch = false;
             let inferenceIncomplete = false;
             let allowComplexConstraintInference = true;
             inferFromTypes(originalSource, originalTarget);
@@ -15576,7 +15576,7 @@ namespace ts {
                                 clearCachedInferences(inferences);
                             }
                         }
-                        inferenceCount++;
+                        inferenceMatch = true;
                         return;
                     }
                     else {
@@ -15668,15 +15668,21 @@ namespace ts {
 
             function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
                 const key = source.id + "," + target.id;
-                const count = visited && visited.get(key);
-                if (count !== undefined) {
-                    inferenceCount += count;
+                const status = visited && visited.get(key);
+                if (status !== undefined) {
+                    if (status & 1) inferenceMatch = true;
+                    if (status & 2) inferenceIncomplete = true;
                     return;
                 }
                 (visited || (visited = createMap<number>())).set(key, 0);
-                const startCount = inferenceCount;
+                const saveInferenceMatch = inferenceMatch;
+                const saveInferenceIncomplete = inferenceIncomplete;
+                inferenceMatch = false;
+                inferenceIncomplete = false;
                 action(source, target);
-                visited.set(key, inferenceCount - startCount);
+                visited.set(key, (inferenceMatch ? 1 : 0) | (inferenceIncomplete ? 2 : 0));
+                inferenceMatch = inferenceMatch || saveInferenceMatch;
+                inferenceIncomplete = inferenceIncomplete || saveInferenceIncomplete;
             }
 
             function inferFromMatchingType(source: Type, targets: Type[], matches: (s: Type, t: Type) => boolean) {
@@ -15759,9 +15765,11 @@ namespace ts {
                         }
                         else {
                             for (let i = 0; i < sources.length; i++) {
-                                const count = inferenceCount;
+                                const saveInferenceMatch = inferenceMatch;
+                                inferenceMatch = false;
                                 inferFromTypes(sources[i], t);
-                                if (count !== inferenceCount) matched[i] = true;
+                                if (inferenceMatch) matched[i] = true;
+                                inferenceMatch = inferenceMatch || saveInferenceMatch;
                             }
                         }
                     }