mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge pull request #34607 from microsoft/fix33490
Fix type inference regression
This commit is contained in:
+37
-21
@@ -17354,10 +17354,7 @@ namespace ts {
|
||||
// inferring a type parameter constraint. Instead, make a lower priority inference from
|
||||
// the full source to whatever remains in the target. For example, when inferring from
|
||||
// string to 'string | T', make a lower priority inference of string for T.
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.NakedTypeVariable;
|
||||
inferFromTypes(source, target);
|
||||
priority = savePriority;
|
||||
inferWithPriority(source, target, InferencePriority.NakedTypeVariable);
|
||||
return;
|
||||
}
|
||||
source = getUnionType(sources);
|
||||
@@ -17459,10 +17456,7 @@ namespace ts {
|
||||
else if ((isLiteralType(source) || source.flags & TypeFlags.String) && target.flags & TypeFlags.Index) {
|
||||
const empty = createEmptyObjectTypeFromStringLiteral(source);
|
||||
contravariant = !contravariant;
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.LiteralKeyof;
|
||||
inferFromTypes(empty, (target as IndexType).type);
|
||||
priority = savePriority;
|
||||
inferWithPriority(empty, (target as IndexType).type, InferencePriority.LiteralKeyof);
|
||||
contravariant = !contravariant;
|
||||
}
|
||||
else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
|
||||
@@ -17514,6 +17508,13 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) {
|
||||
const savePriority = priority;
|
||||
priority |= newPriority;
|
||||
inferFromTypes(source, target);
|
||||
priority = savePriority;
|
||||
}
|
||||
|
||||
function invokeOnce(source: Type, target: Type, action: (source: Type, target: Type) => void) {
|
||||
const key = source.id + "," + target.id;
|
||||
const status = visited && visited.get(key);
|
||||
@@ -17581,6 +17582,18 @@ namespace ts {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function getSingleTypeVariableFromIntersectionTypes(types: Type[]) {
|
||||
let typeVariable: Type | undefined;
|
||||
for (const type of types) {
|
||||
const t = type.flags & TypeFlags.Intersection && find((<IntersectionType>type).types, t => !!getInferenceInfoForType(t));
|
||||
if (!t || typeVariable && t !== typeVariable) {
|
||||
return undefined;
|
||||
}
|
||||
typeVariable = t;
|
||||
}
|
||||
return typeVariable;
|
||||
}
|
||||
|
||||
function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
|
||||
let typeVariableCount = 0;
|
||||
if (targetFlags & TypeFlags.Union) {
|
||||
@@ -17608,6 +17621,16 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (typeVariableCount === 0) {
|
||||
// If every target is an intersection of types containing a single naked type variable,
|
||||
// make a lower priority inference to that type variable. This handles inferring from
|
||||
// 'A | B' to 'T & (X | Y)' where we want to infer 'A | B' for T.
|
||||
const intersectionTypeVariable = getSingleTypeVariableFromIntersectionTypes(targets);
|
||||
if (intersectionTypeVariable) {
|
||||
inferWithPriority(source, intersectionTypeVariable, InferencePriority.NakedTypeVariable);
|
||||
}
|
||||
return;
|
||||
}
|
||||
// If the target has a single naked type variable and no inference circularities were
|
||||
// encountered above (meaning we explored the types fully), create a union of the source
|
||||
// types from which no inferences have been made so far and infer from that union to the
|
||||
@@ -17638,14 +17661,11 @@ namespace ts {
|
||||
// we want to infer string for T, not Promise<string> | string. For intersection types
|
||||
// we only infer to single naked type variables.
|
||||
if (targetFlags & TypeFlags.Intersection ? typeVariableCount === 1 : typeVariableCount > 0) {
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.NakedTypeVariable;
|
||||
for (const t of targets) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
inferFromTypes(source, t);
|
||||
inferWithPriority(source, t, InferencePriority.NakedTypeVariable);
|
||||
}
|
||||
}
|
||||
priority = savePriority;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17666,14 +17686,13 @@ namespace ts {
|
||||
if (inference && !inference.isFixed) {
|
||||
const inferredType = inferTypeForHomomorphicMappedType(source, target, <IndexType>constraintType);
|
||||
if (inferredType) {
|
||||
const savePriority = priority;
|
||||
// We assign a lower priority to inferences made from types containing non-inferrable
|
||||
// types because we may only have a partial result (i.e. we may have failed to make
|
||||
// reverse inferences for some properties).
|
||||
priority |= getObjectFlags(source) & ObjectFlags.NonInferrableType ?
|
||||
InferencePriority.PartialHomomorphicMappedType : InferencePriority.HomomorphicMappedType;
|
||||
inferFromTypes(inferredType, inference.typeParameter);
|
||||
priority = savePriority;
|
||||
inferWithPriority(inferredType, inference.typeParameter,
|
||||
getObjectFlags(source) & ObjectFlags.NonInferrableType ?
|
||||
InferencePriority.PartialHomomorphicMappedType :
|
||||
InferencePriority.HomomorphicMappedType);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -17681,10 +17700,7 @@ namespace ts {
|
||||
if (constraintType.flags & TypeFlags.TypeParameter) {
|
||||
// We're inferring from some source type S to a mapped type { [P in K]: X }, where K is a type
|
||||
// parameter. First infer from 'keyof S' to K.
|
||||
const savePriority = priority;
|
||||
priority |= InferencePriority.MappedTypeConstraint;
|
||||
inferFromTypes(getIndexType(source), constraintType);
|
||||
priority = savePriority;
|
||||
inferWithPriority(getIndexType(source), constraintType, InferencePriority.MappedTypeConstraint);
|
||||
// If K is constrained to a type C, also infer to C. Thus, for a mapped type { [P in K]: X },
|
||||
// where K extends keyof T, we make the same inferences as for a homomorphic mapped type
|
||||
// { [P in keyof T]: X }. This enables us to make meaningful inferences when the target is a
|
||||
|
||||
@@ -52,10 +52,43 @@ let y1 = foo1(sx); // string
|
||||
|
||||
let x2 = foo2(sa); // unknown
|
||||
let y2 = foo2(sx); // { extra: number }
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
declare class Component<P> { props: P }
|
||||
|
||||
export type ComponentClass<P> = new (props: P) => Component<P>;
|
||||
export type FunctionComponent<P> = (props: P) => null;
|
||||
|
||||
export type ComponentType<P> = FunctionComponent<P> | ComponentClass<P>;
|
||||
|
||||
export interface RouteComponentProps { route: string }
|
||||
|
||||
declare function withRouter<
|
||||
P extends RouteComponentProps,
|
||||
C extends ComponentType<P>
|
||||
>(
|
||||
component: C & ComponentType<P>
|
||||
): ComponentClass<Omit<P, keyof RouteComponentProps>>;
|
||||
|
||||
interface Props extends RouteComponentProps { username: string }
|
||||
|
||||
declare const MyComponent: ComponentType<Props>;
|
||||
|
||||
withRouter(MyComponent);
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
type AB<T> = { a: T } | { b: T };
|
||||
|
||||
// T & AB<U> normalizes to T & { a: U } | T & { b: U } below
|
||||
declare function foo<T, U>(obj: T & AB<U>): [T, U];
|
||||
declare let ab: AB<string>;
|
||||
|
||||
let z = foo(ab); // [AB<string>, string]
|
||||
|
||||
|
||||
//// [unionAndIntersectionInference3.js]
|
||||
"use strict";
|
||||
// Repro from #30720
|
||||
concatMaybe([1, 2, 3], 4);
|
||||
// Repros from #32247
|
||||
@@ -70,3 +103,5 @@ let x1 = foo1(sa); // string
|
||||
let y1 = foo1(sx); // string
|
||||
let x2 = foo2(sa); // unknown
|
||||
let y2 = foo2(sx); // { extra: number }
|
||||
withRouter(MyComponent);
|
||||
let z = foo(ab); // [AB<string>, string]
|
||||
|
||||
@@ -205,3 +205,107 @@ let y2 = foo2(sx); // { extra: number }
|
||||
>foo2 : Symbol(foo2, Decl(unionAndIntersectionInference3.ts, 42, 57))
|
||||
>sx : Symbol(sx, Decl(unionAndIntersectionInference3.ts, 46, 11))
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
declare class Component<P> { props: P }
|
||||
>Component : Symbol(Component, Decl(unionAndIntersectionInference3.ts, 52, 18))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 56, 24))
|
||||
>props : Symbol(Component.props, Decl(unionAndIntersectionInference3.ts, 56, 28))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 56, 24))
|
||||
|
||||
export type ComponentClass<P> = new (props: P) => Component<P>;
|
||||
>ComponentClass : Symbol(ComponentClass, Decl(unionAndIntersectionInference3.ts, 56, 39))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 58, 27))
|
||||
>props : Symbol(props, Decl(unionAndIntersectionInference3.ts, 58, 37))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 58, 27))
|
||||
>Component : Symbol(Component, Decl(unionAndIntersectionInference3.ts, 52, 18))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 58, 27))
|
||||
|
||||
export type FunctionComponent<P> = (props: P) => null;
|
||||
>FunctionComponent : Symbol(FunctionComponent, Decl(unionAndIntersectionInference3.ts, 58, 63))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 59, 30))
|
||||
>props : Symbol(props, Decl(unionAndIntersectionInference3.ts, 59, 36))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 59, 30))
|
||||
|
||||
export type ComponentType<P> = FunctionComponent<P> | ComponentClass<P>;
|
||||
>ComponentType : Symbol(ComponentType, Decl(unionAndIntersectionInference3.ts, 59, 54))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 61, 26))
|
||||
>FunctionComponent : Symbol(FunctionComponent, Decl(unionAndIntersectionInference3.ts, 58, 63))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 61, 26))
|
||||
>ComponentClass : Symbol(ComponentClass, Decl(unionAndIntersectionInference3.ts, 56, 39))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 61, 26))
|
||||
|
||||
export interface RouteComponentProps { route: string }
|
||||
>RouteComponentProps : Symbol(RouteComponentProps, Decl(unionAndIntersectionInference3.ts, 61, 72))
|
||||
>route : Symbol(RouteComponentProps.route, Decl(unionAndIntersectionInference3.ts, 63, 38))
|
||||
|
||||
declare function withRouter<
|
||||
>withRouter : Symbol(withRouter, Decl(unionAndIntersectionInference3.ts, 63, 54))
|
||||
|
||||
P extends RouteComponentProps,
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 65, 28))
|
||||
>RouteComponentProps : Symbol(RouteComponentProps, Decl(unionAndIntersectionInference3.ts, 61, 72))
|
||||
|
||||
C extends ComponentType<P>
|
||||
>C : Symbol(C, Decl(unionAndIntersectionInference3.ts, 66, 32))
|
||||
>ComponentType : Symbol(ComponentType, Decl(unionAndIntersectionInference3.ts, 59, 54))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 65, 28))
|
||||
|
||||
>(
|
||||
component: C & ComponentType<P>
|
||||
>component : Symbol(component, Decl(unionAndIntersectionInference3.ts, 68, 2))
|
||||
>C : Symbol(C, Decl(unionAndIntersectionInference3.ts, 66, 32))
|
||||
>ComponentType : Symbol(ComponentType, Decl(unionAndIntersectionInference3.ts, 59, 54))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 65, 28))
|
||||
|
||||
): ComponentClass<Omit<P, keyof RouteComponentProps>>;
|
||||
>ComponentClass : Symbol(ComponentClass, Decl(unionAndIntersectionInference3.ts, 56, 39))
|
||||
>Omit : Symbol(Omit, Decl(lib.es5.d.ts, --, --))
|
||||
>P : Symbol(P, Decl(unionAndIntersectionInference3.ts, 65, 28))
|
||||
>RouteComponentProps : Symbol(RouteComponentProps, Decl(unionAndIntersectionInference3.ts, 61, 72))
|
||||
|
||||
interface Props extends RouteComponentProps { username: string }
|
||||
>Props : Symbol(Props, Decl(unionAndIntersectionInference3.ts, 70, 54))
|
||||
>RouteComponentProps : Symbol(RouteComponentProps, Decl(unionAndIntersectionInference3.ts, 61, 72))
|
||||
>username : Symbol(Props.username, Decl(unionAndIntersectionInference3.ts, 72, 45))
|
||||
|
||||
declare const MyComponent: ComponentType<Props>;
|
||||
>MyComponent : Symbol(MyComponent, Decl(unionAndIntersectionInference3.ts, 74, 13))
|
||||
>ComponentType : Symbol(ComponentType, Decl(unionAndIntersectionInference3.ts, 59, 54))
|
||||
>Props : Symbol(Props, Decl(unionAndIntersectionInference3.ts, 70, 54))
|
||||
|
||||
withRouter(MyComponent);
|
||||
>withRouter : Symbol(withRouter, Decl(unionAndIntersectionInference3.ts, 63, 54))
|
||||
>MyComponent : Symbol(MyComponent, Decl(unionAndIntersectionInference3.ts, 74, 13))
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
type AB<T> = { a: T } | { b: T };
|
||||
>AB : Symbol(AB, Decl(unionAndIntersectionInference3.ts, 76, 24))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 80, 8))
|
||||
>a : Symbol(a, Decl(unionAndIntersectionInference3.ts, 80, 14))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 80, 8))
|
||||
>b : Symbol(b, Decl(unionAndIntersectionInference3.ts, 80, 25))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 80, 8))
|
||||
|
||||
// T & AB<U> normalizes to T & { a: U } | T & { b: U } below
|
||||
declare function foo<T, U>(obj: T & AB<U>): [T, U];
|
||||
>foo : Symbol(foo, Decl(unionAndIntersectionInference3.ts, 80, 33))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 83, 21))
|
||||
>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 83, 23))
|
||||
>obj : Symbol(obj, Decl(unionAndIntersectionInference3.ts, 83, 27))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 83, 21))
|
||||
>AB : Symbol(AB, Decl(unionAndIntersectionInference3.ts, 76, 24))
|
||||
>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 83, 23))
|
||||
>T : Symbol(T, Decl(unionAndIntersectionInference3.ts, 83, 21))
|
||||
>U : Symbol(U, Decl(unionAndIntersectionInference3.ts, 83, 23))
|
||||
|
||||
declare let ab: AB<string>;
|
||||
>ab : Symbol(ab, Decl(unionAndIntersectionInference3.ts, 84, 11))
|
||||
>AB : Symbol(AB, Decl(unionAndIntersectionInference3.ts, 76, 24))
|
||||
|
||||
let z = foo(ab); // [AB<string>, string]
|
||||
>z : Symbol(z, Decl(unionAndIntersectionInference3.ts, 86, 3))
|
||||
>foo : Symbol(foo, Decl(unionAndIntersectionInference3.ts, 80, 33))
|
||||
>ab : Symbol(ab, Decl(unionAndIntersectionInference3.ts, 84, 11))
|
||||
|
||||
|
||||
@@ -135,3 +135,67 @@ let y2 = foo2(sx); // { extra: number }
|
||||
>foo2 : <T>(obj: string[] & T) => T
|
||||
>sx : string[] & { extra: number; }
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
declare class Component<P> { props: P }
|
||||
>Component : Component<P>
|
||||
>props : P
|
||||
|
||||
export type ComponentClass<P> = new (props: P) => Component<P>;
|
||||
>ComponentClass : ComponentClass<P>
|
||||
>props : P
|
||||
|
||||
export type FunctionComponent<P> = (props: P) => null;
|
||||
>FunctionComponent : FunctionComponent<P>
|
||||
>props : P
|
||||
>null : null
|
||||
|
||||
export type ComponentType<P> = FunctionComponent<P> | ComponentClass<P>;
|
||||
>ComponentType : ComponentType<P>
|
||||
|
||||
export interface RouteComponentProps { route: string }
|
||||
>route : string
|
||||
|
||||
declare function withRouter<
|
||||
>withRouter : <P extends RouteComponentProps, C extends ComponentType<P>>(component: (C & FunctionComponent<P>) | (C & ComponentClass<P>)) => ComponentClass<Pick<P, Exclude<keyof P, "route">>>
|
||||
|
||||
P extends RouteComponentProps,
|
||||
C extends ComponentType<P>
|
||||
>(
|
||||
component: C & ComponentType<P>
|
||||
>component : (C & FunctionComponent<P>) | (C & ComponentClass<P>)
|
||||
|
||||
): ComponentClass<Omit<P, keyof RouteComponentProps>>;
|
||||
|
||||
interface Props extends RouteComponentProps { username: string }
|
||||
>username : string
|
||||
|
||||
declare const MyComponent: ComponentType<Props>;
|
||||
>MyComponent : ComponentType<Props>
|
||||
|
||||
withRouter(MyComponent);
|
||||
>withRouter(MyComponent) : ComponentClass<Pick<Props, "username">>
|
||||
>withRouter : <P extends RouteComponentProps, C extends ComponentType<P>>(component: (C & FunctionComponent<P>) | (C & ComponentClass<P>)) => ComponentClass<Pick<P, Exclude<keyof P, "route">>>
|
||||
>MyComponent : ComponentType<Props>
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
type AB<T> = { a: T } | { b: T };
|
||||
>AB : AB<T>
|
||||
>a : T
|
||||
>b : T
|
||||
|
||||
// T & AB<U> normalizes to T & { a: U } | T & { b: U } below
|
||||
declare function foo<T, U>(obj: T & AB<U>): [T, U];
|
||||
>foo : <T, U>(obj: (T & { a: U; }) | (T & { b: U; })) => [T, U]
|
||||
>obj : (T & { a: U; }) | (T & { b: U; })
|
||||
|
||||
declare let ab: AB<string>;
|
||||
>ab : AB<string>
|
||||
|
||||
let z = foo(ab); // [AB<string>, string]
|
||||
>z : [AB<string>, string]
|
||||
>foo(ab) : [AB<string>, string]
|
||||
>foo : <T, U>(obj: (T & { a: U; }) | (T & { b: U; })) => [T, U]
|
||||
>ab : AB<string>
|
||||
|
||||
|
||||
+34
@@ -54,3 +54,37 @@ let y1 = foo1(sx); // string
|
||||
|
||||
let x2 = foo2(sa); // unknown
|
||||
let y2 = foo2(sx); // { extra: number }
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
declare class Component<P> { props: P }
|
||||
|
||||
export type ComponentClass<P> = new (props: P) => Component<P>;
|
||||
export type FunctionComponent<P> = (props: P) => null;
|
||||
|
||||
export type ComponentType<P> = FunctionComponent<P> | ComponentClass<P>;
|
||||
|
||||
export interface RouteComponentProps { route: string }
|
||||
|
||||
declare function withRouter<
|
||||
P extends RouteComponentProps,
|
||||
C extends ComponentType<P>
|
||||
>(
|
||||
component: C & ComponentType<P>
|
||||
): ComponentClass<Omit<P, keyof RouteComponentProps>>;
|
||||
|
||||
interface Props extends RouteComponentProps { username: string }
|
||||
|
||||
declare const MyComponent: ComponentType<Props>;
|
||||
|
||||
withRouter(MyComponent);
|
||||
|
||||
// Repro from #33490
|
||||
|
||||
type AB<T> = { a: T } | { b: T };
|
||||
|
||||
// T & AB<U> normalizes to T & { a: U } | T & { b: U } below
|
||||
declare function foo<T, U>(obj: T & AB<U>): [T, U];
|
||||
declare let ab: AB<string>;
|
||||
|
||||
let z = foo(ab); // [AB<string>, string]
|
||||
|
||||
Reference in New Issue
Block a user