Force inner indexed access simplification during inference, if possible (#28420)

* Force inner indexed access simplification during inference, if possible

* rename

* Refactor to used shared implementation of distribution
This commit is contained in:
Wesley Wigham 2018-11-12 14:39:43 -08:00 committed by GitHub
parent 995f746c92
commit 13234b62f5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 171 additions and 13 deletions

View file

@ -9533,6 +9533,17 @@ namespace ts {
return type.flags & TypeFlags.IndexedAccess ? getSimplifiedIndexedAccessType(<IndexedAccessType>type) : type;
}
function distributeIndexOverObjectType(objectType: Type, indexType: Type) {
// (T | U)[K] -> T[K] | U[K]
if (objectType.flags & TypeFlags.Union) {
return mapType(objectType, t => getSimplifiedType(getIndexedAccessType(t, indexType)));
}
// (T & U)[K] -> T[K] & U[K]
if (objectType.flags & TypeFlags.Intersection) {
return getIntersectionType(map((objectType as IntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))));
}
}
// Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
// the type itself if no transformation is possible.
function getSimplifiedIndexedAccessType(type: IndexedAccessType): Type {
@ -9550,13 +9561,9 @@ namespace ts {
}
// Only do the inner distributions if the index can no longer be instantiated to cause index distribution again
if (!(indexType.flags & TypeFlags.Instantiable)) {
// (T | U)[K] -> T[K] | U[K]
if (objectType.flags & TypeFlags.Union) {
return type.simplified = mapType(objectType, t => getSimplifiedType(getIndexedAccessType(t, indexType)));
}
// (T & U)[K] -> T[K] & U[K]
if (objectType.flags & TypeFlags.Intersection) {
return type.simplified = getIntersectionType(map((objectType as IntersectionType).types, t => getSimplifiedType(getIndexedAccessType(t, indexType))));
const simplified = distributeIndexOverObjectType(objectType, indexType);
if (simplified) {
return type.simplified = simplified;
}
}
// So ultimately:
@ -13888,10 +13895,17 @@ 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<boolean>())).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 simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType), indexType);
if (simplified && simplified !== target) {
inferFromTypesOnce(source, simplified);
}
}
}
}
@ -14014,6 +14028,14 @@ namespace ts {
}
}
}
function inferFromTypesOnce(source: Type, target: Type) {
const key = source.id + "," + target.id;
if (!visited || !visited.get(key)) {
(visited || (visited = createMap<boolean>())).set(key, true);
inferFromTypes(source, target);
}
}
}
function inferFromContravariantTypes(source: Type, target: Type) {

View file

@ -20,8 +20,8 @@ class C<T> 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

View file

@ -0,0 +1,38 @@
//// [tsxReactPropsInferenceSucceedsOnIntersections.tsx]
/// <reference path="/.lib/react16.d.ts" />
import React from "react";
export type ButtonProps<T = {}> = React.ButtonHTMLAttributes<HTMLButtonElement> & {
outline?: boolean;
} & T;
declare class Button<T = {}> extends React.Component<ButtonProps<T>> { }
interface CustomButtonProps extends ButtonProps {
customProp: string;
}
const CustomButton: React.SFC<CustomButtonProps> = props => <Button {...props} />;
//// [tsxReactPropsInferenceSucceedsOnIntersections.js]
"use strict";
/// <reference path="react16.d.ts" />
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
var react_1 = __importDefault(require("react"));
var CustomButton = function (props) { return react_1["default"].createElement(Button, __assign({}, props)); };

View file

@ -0,0 +1,45 @@
=== tests/cases/compiler/tsxReactPropsInferenceSucceedsOnIntersections.tsx ===
/// <reference path="react16.d.ts" />
import React from "react";
>React : Symbol(React, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 2, 6))
export type ButtonProps<T = {}> = React.ButtonHTMLAttributes<HTMLButtonElement> & {
>ButtonProps : Symbol(ButtonProps, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 2, 26))
>T : Symbol(T, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 4, 24))
>React : Symbol(React, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 2, 6))
>ButtonHTMLAttributes : Symbol(React.ButtonHTMLAttributes, Decl(react16.d.ts, 1437, 9))
>HTMLButtonElement : Symbol(HTMLButtonElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
outline?: boolean;
>outline : Symbol(outline, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 4, 83))
} & T;
>T : Symbol(T, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 4, 24))
declare class Button<T = {}> extends React.Component<ButtonProps<T>> { }
>Button : Symbol(Button, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 6, 6))
>T : Symbol(T, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 8, 21))
>React.Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>React : Symbol(React, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 2, 6))
>Component : Symbol(React.Component, Decl(react16.d.ts, 345, 54), Decl(react16.d.ts, 349, 94))
>ButtonProps : Symbol(ButtonProps, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 2, 26))
>T : Symbol(T, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 8, 21))
interface CustomButtonProps extends ButtonProps {
>CustomButtonProps : Symbol(CustomButtonProps, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 8, 72))
>ButtonProps : Symbol(ButtonProps, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 2, 26))
customProp: string;
>customProp : Symbol(CustomButtonProps.customProp, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 10, 49))
}
const CustomButton: React.SFC<CustomButtonProps> = props => <Button {...props} />;
>CustomButton : Symbol(CustomButton, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 14, 5))
>React : Symbol(React, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 2, 6))
>SFC : Symbol(React.SFC, Decl(react16.d.ts, 400, 9))
>CustomButtonProps : Symbol(CustomButtonProps, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 8, 72))
>props : Symbol(props, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 14, 50))
>Button : Symbol(Button, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 6, 6))
>props : Symbol(props, Decl(tsxReactPropsInferenceSucceedsOnIntersections.tsx, 14, 50))

View file

@ -0,0 +1,35 @@
=== tests/cases/compiler/tsxReactPropsInferenceSucceedsOnIntersections.tsx ===
/// <reference path="react16.d.ts" />
import React from "react";
>React : typeof React
export type ButtonProps<T = {}> = React.ButtonHTMLAttributes<HTMLButtonElement> & {
>ButtonProps : ButtonProps<T>
>React : any
outline?: boolean;
>outline : boolean | undefined
} & T;
declare class Button<T = {}> extends React.Component<ButtonProps<T>> { }
>Button : Button<T>
>React.Component : React.Component<ButtonProps<T>, {}, any>
>React : typeof React
>Component : typeof React.Component
interface CustomButtonProps extends ButtonProps {
customProp: string;
>customProp : string
}
const CustomButton: React.SFC<CustomButtonProps> = props => <Button {...props} />;
>CustomButton : React.StatelessComponent<CustomButtonProps>
>React : any
>props => <Button {...props} /> : (props: CustomButtonProps & { children?: React.ReactNode; }) => JSX.Element
>props : CustomButtonProps & { children?: React.ReactNode; }
><Button {...props} /> : JSX.Element
>Button : typeof Button
>props : CustomButtonProps & { children?: React.ReactNode; }

View file

@ -0,0 +1,18 @@
// @jsx: react
// @esModuleInterop: true
// @strictNullChecks: true
/// <reference path="/.lib/react16.d.ts" />
import React from "react";
export type ButtonProps<T = {}> = React.ButtonHTMLAttributes<HTMLButtonElement> & {
outline?: boolean;
} & T;
declare class Button<T = {}> extends React.Component<ButtonProps<T>> { }
interface CustomButtonProps extends ButtonProps {
customProp: string;
}
const CustomButton: React.SFC<CustomButtonProps> = props => <Button {...props} />;