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; } } }