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:
parent
995f746c92
commit
13234b62f5
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)); };
|
|
@ -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))
|
||||
|
|
@ -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; }
|
||||
|
|
@ -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} />;
|
Loading…
Reference in a new issue