From 0d896c12c1e21ad7934a5cfed180b1f8e38e2948 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Thu, 8 Nov 2018 15:03:46 -0800 Subject: [PATCH 1/3] Force inner indexed access simplification during inference, if possible --- src/compiler/checker.ts | 28 ++++++++++-- ...checkJsxIntersectionElementPropsType.types | 4 +- ...actPropsInferenceSuceedsOnIntersections.js | 38 ++++++++++++++++ ...opsInferenceSuceedsOnIntersections.symbols | 45 +++++++++++++++++++ ...PropsInferenceSuceedsOnIntersections.types | 35 +++++++++++++++ ...ctPropsInferenceSuceedsOnIntersections.tsx | 18 ++++++++ 6 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/tsxReactPropsInferenceSuceedsOnIntersections.js create mode 100644 tests/baselines/reference/tsxReactPropsInferenceSuceedsOnIntersections.symbols create mode 100644 tests/baselines/reference/tsxReactPropsInferenceSuceedsOnIntersections.types create mode 100644 tests/cases/compiler/tsxReactPropsInferenceSuceedsOnIntersections.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 103200600b219..1a6fe38563804 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13833,10 +13833,22 @@ namespace ts { // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine const simplified = getSimplifiedType(target); if (simplified !== target) { - const key = source.id + "," + simplified.id; - if (!visited || !visited.get(key)) { - (visited || (visited = createMap())).set(key, true); - inferFromTypes(source, simplified); + inferFromTypesOnce(source, simplified); + } + else if (target.flags & TypeFlags.IndexedAccess) { + const indexType = getSimplifiedType((target as IndexedAccessType).indexType); + // Generally simplifications of instantiable indexes are avoided to keep relationship checking correct, however if our target is an access, we can consider + // that key of that access to be "instantiated", since we're looking to find the infernce goal in any way we can. + if (indexType.flags & TypeFlags.Instantiable) { + const objectType = getSimplifiedType((target as IndexedAccessType).objectType); + // (T | U)[K] -> T[K] | U[K] + if (objectType.flags & TypeFlags.Union) { + inferFromTypesOnce(source, mapType(objectType, t => getSimplifiedType(getIndexedAccessType(t, indexType)))); + } + // (T & U)[K] -> T[K] & U[K] + else if (objectType.flags & TypeFlags.Intersection) { + inferFromTypesOnce(source, getIntersectionType(map((objectType as IntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))))); + } } } } @@ -13959,6 +13971,14 @@ namespace ts { } } } + + function inferFromTypesOnce(source: Type, target: Type) { + const key = source.id + "," + target.id; + if (!visited || !visited.get(key)) { + (visited || (visited = createMap())).set(key, true); + inferFromTypes(source, target); + } + } } function inferFromContravariantTypes(source: Type, target: Type) { diff --git a/tests/baselines/reference/checkJsxIntersectionElementPropsType.types b/tests/baselines/reference/checkJsxIntersectionElementPropsType.types index 0e95dbed36166..187d0f274a409 100644 --- a/tests/baselines/reference/checkJsxIntersectionElementPropsType.types +++ b/tests/baselines/reference/checkJsxIntersectionElementPropsType.types @@ -20,8 +20,8 @@ class C extends Component<{ x?: boolean; } & T> {} >x : boolean | undefined const y = new C({foobar: "example"}); ->y : C<{ foobar: {}; }> ->new C({foobar: "example"}) : C<{ foobar: {}; }> +>y : C<{ foobar: string; }> +>new C({foobar: "example"}) : C<{ foobar: string; }> >C : typeof C >{foobar: "example"} : { foobar: string; } >foobar : string diff --git a/tests/baselines/reference/tsxReactPropsInferenceSuceedsOnIntersections.js b/tests/baselines/reference/tsxReactPropsInferenceSuceedsOnIntersections.js new file mode 100644 index 0000000000000..e17c6870aa27a --- /dev/null +++ b/tests/baselines/reference/tsxReactPropsInferenceSuceedsOnIntersections.js @@ -0,0 +1,38 @@ +//// [tsxReactPropsInferenceSuceedsOnIntersections.tsx] +/// + +import React from "react"; + +export type ButtonProps = React.ButtonHTMLAttributes & { + outline?: boolean; +} & T; + +declare class Button extends React.Component> { } + +interface CustomButtonProps extends ButtonProps { + customProp: string; +} + +const CustomButton: React.SFC = props =>