Skip to content

Commit 28fbc0a

Browse files
authored
Filter return type inferences by constraint applicability (#58910)
1 parent 5026c66 commit 28fbc0a

13 files changed

+889
-2
lines changed

src/compiler/checker.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27576,10 +27576,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2757627576
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
2757727577
if (constraint) {
2757827578
const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper);
27579-
if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
27579+
if (inferredType) {
27580+
const constraintWithThis = getTypeWithThisArgument(instantiatedConstraint, inferredType);
27581+
if (!context.compareTypes(inferredType, constraintWithThis)) {
27582+
// If we have a pure return type inference, we may succeed by removing constituents of the inferred type
27583+
// that aren't assignable to the constraint type (pure return type inferences are speculation anyway).
27584+
const filteredByConstraint = inference.priority === InferencePriority.ReturnType ? filterType(inferredType, t => !!context.compareTypes(t, constraintWithThis)) : neverType;
27585+
inferredType = !(filteredByConstraint.flags & TypeFlags.Never) ? filteredByConstraint : undefined;
27586+
}
27587+
}
27588+
if (!inferredType) {
2758027589
// If the fallback type satisfies the constraint, we pick it. Otherwise, we pick the constraint.
27581-
inference.inferredType = fallbackType && context.compareTypes(fallbackType, getTypeWithThisArgument(instantiatedConstraint, fallbackType)) ? fallbackType : instantiatedConstraint;
27590+
inferredType = fallbackType && context.compareTypes(fallbackType, getTypeWithThisArgument(instantiatedConstraint, fallbackType)) ? fallbackType : instantiatedConstraint;
2758227591
}
27592+
inference.inferredType = inferredType;
2758327593
}
2758427594
clearActiveMapperCaches();
2758527595
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//// [tests/cases/compiler/inferenceContextualReturnTypeUnion1.ts] ////
2+
3+
=== inferenceContextualReturnTypeUnion1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/50719
5+
6+
declare function useCallback1<T extends Function>(fn: T): T;
7+
>useCallback1 : Symbol(useCallback1, Decl(inferenceContextualReturnTypeUnion1.ts, 0, 0))
8+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion1.ts, 2, 30))
9+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
10+
>fn : Symbol(fn, Decl(inferenceContextualReturnTypeUnion1.ts, 2, 50))
11+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion1.ts, 2, 30))
12+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion1.ts, 2, 30))
13+
14+
declare function ex2(callback?: (x: number) => void): void;
15+
>ex2 : Symbol(ex2, Decl(inferenceContextualReturnTypeUnion1.ts, 2, 60))
16+
>callback : Symbol(callback, Decl(inferenceContextualReturnTypeUnion1.ts, 4, 21))
17+
>x : Symbol(x, Decl(inferenceContextualReturnTypeUnion1.ts, 4, 33))
18+
19+
ex2(useCallback1((x) => {}));
20+
>ex2 : Symbol(ex2, Decl(inferenceContextualReturnTypeUnion1.ts, 2, 60))
21+
>useCallback1 : Symbol(useCallback1, Decl(inferenceContextualReturnTypeUnion1.ts, 0, 0))
22+
>x : Symbol(x, Decl(inferenceContextualReturnTypeUnion1.ts, 5, 18))
23+
24+
declare function ex3(callback: ((x: number) => void) | 5): void;
25+
>ex3 : Symbol(ex3, Decl(inferenceContextualReturnTypeUnion1.ts, 5, 29))
26+
>callback : Symbol(callback, Decl(inferenceContextualReturnTypeUnion1.ts, 7, 21))
27+
>x : Symbol(x, Decl(inferenceContextualReturnTypeUnion1.ts, 7, 33))
28+
29+
ex3(useCallback1((x) => {}));
30+
>ex3 : Symbol(ex3, Decl(inferenceContextualReturnTypeUnion1.ts, 5, 29))
31+
>useCallback1 : Symbol(useCallback1, Decl(inferenceContextualReturnTypeUnion1.ts, 0, 0))
32+
>x : Symbol(x, Decl(inferenceContextualReturnTypeUnion1.ts, 8, 18))
33+
34+
// https://github.com/microsoft/TypeScript/issues/41461
35+
36+
declare function useCallback2<T extends (...args: any[]) => any>(
37+
>useCallback2 : Symbol(useCallback2, Decl(inferenceContextualReturnTypeUnion1.ts, 8, 29))
38+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion1.ts, 12, 30))
39+
>args : Symbol(args, Decl(inferenceContextualReturnTypeUnion1.ts, 12, 41))
40+
41+
callback: T,
42+
>callback : Symbol(callback, Decl(inferenceContextualReturnTypeUnion1.ts, 12, 65))
43+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion1.ts, 12, 30))
44+
45+
): T;
46+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion1.ts, 12, 30))
47+
48+
const test: ((x: string) => void) | undefined = useCallback2((x) => {});
49+
>test : Symbol(test, Decl(inferenceContextualReturnTypeUnion1.ts, 15, 5))
50+
>x : Symbol(x, Decl(inferenceContextualReturnTypeUnion1.ts, 15, 14))
51+
>useCallback2 : Symbol(useCallback2, Decl(inferenceContextualReturnTypeUnion1.ts, 8, 29))
52+
>x : Symbol(x, Decl(inferenceContextualReturnTypeUnion1.ts, 15, 62))
53+
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
//// [tests/cases/compiler/inferenceContextualReturnTypeUnion1.ts] ////
2+
3+
=== inferenceContextualReturnTypeUnion1.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/50719
5+
6+
declare function useCallback1<T extends Function>(fn: T): T;
7+
>useCallback1 : <T extends Function>(fn: T) => T
8+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
9+
>fn : T
10+
> : ^
11+
12+
declare function ex2(callback?: (x: number) => void): void;
13+
>ex2 : (callback?: (x: number) => void) => void
14+
> : ^ ^^^ ^^^^^
15+
>callback : ((x: number) => void) | undefined
16+
> : ^^ ^^ ^^^^^ ^^^^^^^^^^^^^
17+
>x : number
18+
> : ^^^^^^
19+
20+
ex2(useCallback1((x) => {}));
21+
>ex2(useCallback1((x) => {})) : void
22+
> : ^^^^
23+
>ex2 : (callback?: (x: number) => void) => void
24+
> : ^ ^^^ ^^^^^
25+
>useCallback1((x) => {}) : (x: number) => void
26+
> : ^ ^^^^^^^^^^^^^^^^^
27+
>useCallback1 : <T extends Function>(fn: T) => T
28+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
29+
>(x) => {} : (x: number) => void
30+
> : ^ ^^^^^^^^^^^^^^^^^
31+
>x : number
32+
> : ^^^^^^
33+
34+
declare function ex3(callback: ((x: number) => void) | 5): void;
35+
>ex3 : (callback: ((x: number) => void) | 5) => void
36+
> : ^ ^^ ^^^^^
37+
>callback : ((x: number) => void) | 5
38+
> : ^^ ^^ ^^^^^ ^^^^^
39+
>x : number
40+
> : ^^^^^^
41+
42+
ex3(useCallback1((x) => {}));
43+
>ex3(useCallback1((x) => {})) : void
44+
> : ^^^^
45+
>ex3 : (callback: ((x: number) => void) | 5) => void
46+
> : ^ ^^ ^^^^^
47+
>useCallback1((x) => {}) : (x: number) => void
48+
> : ^ ^^^^^^^^^^^^^^^^^
49+
>useCallback1 : <T extends Function>(fn: T) => T
50+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
51+
>(x) => {} : (x: number) => void
52+
> : ^ ^^^^^^^^^^^^^^^^^
53+
>x : number
54+
> : ^^^^^^
55+
56+
// https://github.com/microsoft/TypeScript/issues/41461
57+
58+
declare function useCallback2<T extends (...args: any[]) => any>(
59+
>useCallback2 : <T extends (...args: any[]) => any>(callback: T) => T
60+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
61+
>args : any[]
62+
> : ^^^^^
63+
64+
callback: T,
65+
>callback : T
66+
> : ^
67+
68+
): T;
69+
const test: ((x: string) => void) | undefined = useCallback2((x) => {});
70+
>test : ((x: string) => void) | undefined
71+
> : ^^ ^^ ^^^^^ ^^^^^^^^^^^^^
72+
>x : string
73+
> : ^^^^^^
74+
>useCallback2((x) => {}) : (x: string) => void
75+
> : ^ ^^^^^^^^^^^^^^^^^
76+
>useCallback2 : <T extends (...args: any[]) => any>(callback: T) => T
77+
> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^
78+
>(x) => {} : (x: string) => void
79+
> : ^ ^^^^^^^^^^^^^^^^^
80+
>x : string
81+
> : ^^^^^^
82+
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
//// [tests/cases/compiler/inferenceContextualReturnTypeUnion2.ts] ////
2+
3+
=== inferenceContextualReturnTypeUnion2.ts ===
4+
type Query = (
5+
>Query : Symbol(Query, Decl(inferenceContextualReturnTypeUnion2.ts, 0, 0))
6+
7+
container: HTMLElement,
8+
>container : Symbol(container, Decl(inferenceContextualReturnTypeUnion2.ts, 0, 14))
9+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
10+
11+
...args: any[]
12+
>args : Symbol(args, Decl(inferenceContextualReturnTypeUnion2.ts, 1, 25))
13+
14+
) =>
15+
| Error
16+
>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2022.error.d.ts, --, --))
17+
18+
| HTMLElement
19+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
20+
21+
| HTMLElement[]
22+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
23+
24+
| Promise<HTMLElement[]>
25+
>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, --, --))
26+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
27+
28+
| Promise<HTMLElement>
29+
>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, --, --))
30+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
31+
32+
| null;
33+
34+
interface Queries {
35+
>Queries : Symbol(Queries, Decl(inferenceContextualReturnTypeUnion2.ts, 9, 9))
36+
37+
[T: string]: Query;
38+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion2.ts, 12, 3))
39+
>Query : Symbol(Query, Decl(inferenceContextualReturnTypeUnion2.ts, 0, 0))
40+
}
41+
42+
type FindByText<T extends HTMLElement = HTMLElement> = (
43+
>FindByText : Symbol(FindByText, Decl(inferenceContextualReturnTypeUnion2.ts, 13, 1))
44+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion2.ts, 15, 16))
45+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
46+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
47+
48+
container: HTMLElement,
49+
>container : Symbol(container, Decl(inferenceContextualReturnTypeUnion2.ts, 15, 56))
50+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
51+
52+
text: string,
53+
>text : Symbol(text, Decl(inferenceContextualReturnTypeUnion2.ts, 16, 25))
54+
55+
) => Promise<T>;
56+
>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, --, --))
57+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion2.ts, 15, 16))
58+
59+
declare function findByLabelText<T extends HTMLElement = HTMLElement>(
60+
>findByLabelText : Symbol(findByLabelText, Decl(inferenceContextualReturnTypeUnion2.ts, 18, 16))
61+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion2.ts, 20, 33))
62+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
63+
>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --))
64+
65+
...args: Parameters<FindByText<T>>
66+
>args : Symbol(args, Decl(inferenceContextualReturnTypeUnion2.ts, 20, 70))
67+
>Parameters : Symbol(Parameters, Decl(lib.es5.d.ts, --, --))
68+
>FindByText : Symbol(FindByText, Decl(inferenceContextualReturnTypeUnion2.ts, 13, 1))
69+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion2.ts, 20, 33))
70+
71+
): ReturnType<FindByText<T>>;
72+
>ReturnType : Symbol(ReturnType, Decl(lib.es5.d.ts, --, --))
73+
>FindByText : Symbol(FindByText, Decl(inferenceContextualReturnTypeUnion2.ts, 13, 1))
74+
>T : Symbol(T, Decl(inferenceContextualReturnTypeUnion2.ts, 20, 33))
75+
76+
const queries = {
77+
>queries : Symbol(queries, Decl(inferenceContextualReturnTypeUnion2.ts, 24, 5))
78+
79+
findByLabelText,
80+
>findByLabelText : Symbol(findByLabelText, Decl(inferenceContextualReturnTypeUnion2.ts, 24, 17))
81+
82+
};
83+
84+
type MapQueries<Q extends Queries = typeof queries> = {
85+
>MapQueries : Symbol(MapQueries, Decl(inferenceContextualReturnTypeUnion2.ts, 26, 2))
86+
>Q : Symbol(Q, Decl(inferenceContextualReturnTypeUnion2.ts, 28, 16))
87+
>Queries : Symbol(Queries, Decl(inferenceContextualReturnTypeUnion2.ts, 9, 9))
88+
>queries : Symbol(queries, Decl(inferenceContextualReturnTypeUnion2.ts, 24, 5))
89+
90+
[P in keyof Q]: Q[P];
91+
>P : Symbol(P, Decl(inferenceContextualReturnTypeUnion2.ts, 29, 3))
92+
>Q : Symbol(Q, Decl(inferenceContextualReturnTypeUnion2.ts, 28, 16))
93+
>Q : Symbol(Q, Decl(inferenceContextualReturnTypeUnion2.ts, 28, 16))
94+
>P : Symbol(P, Decl(inferenceContextualReturnTypeUnion2.ts, 29, 3))
95+
96+
};
97+
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
//// [tests/cases/compiler/inferenceContextualReturnTypeUnion2.ts] ////
2+
3+
=== inferenceContextualReturnTypeUnion2.ts ===
4+
type Query = (
5+
>Query : Query
6+
> : ^^^^^
7+
8+
container: HTMLElement,
9+
>container : HTMLElement
10+
> : ^^^^^^^^^^^
11+
12+
...args: any[]
13+
>args : any[]
14+
> : ^^^^^
15+
16+
) =>
17+
| Error
18+
| HTMLElement
19+
| HTMLElement[]
20+
| Promise<HTMLElement[]>
21+
| Promise<HTMLElement>
22+
| null;
23+
24+
interface Queries {
25+
[T: string]: Query;
26+
>T : string
27+
> : ^^^^^^
28+
}
29+
30+
type FindByText<T extends HTMLElement = HTMLElement> = (
31+
>FindByText : FindByText<T>
32+
> : ^^^^^^^^^^^^^
33+
34+
container: HTMLElement,
35+
>container : HTMLElement
36+
> : ^^^^^^^^^^^
37+
38+
text: string,
39+
>text : string
40+
> : ^^^^^^
41+
42+
) => Promise<T>;
43+
44+
declare function findByLabelText<T extends HTMLElement = HTMLElement>(
45+
>findByLabelText : <T extends HTMLElement = HTMLElement>(container: HTMLElement, text: string) => ReturnType<FindByText<T>>
46+
> : ^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
47+
48+
...args: Parameters<FindByText<T>>
49+
>args : [container: HTMLElement, text: string]
50+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
51+
52+
): ReturnType<FindByText<T>>;
53+
54+
const queries = {
55+
>queries : { findByLabelText: <T extends HTMLElement = HTMLElement>(container: HTMLElement, text: string) => ReturnType<FindByText<T>>; }
56+
> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^
57+
>{ findByLabelText,} : { findByLabelText: <T extends HTMLElement = HTMLElement>(container: HTMLElement, text: string) => ReturnType<FindByText<T>>; }
58+
> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^
59+
60+
findByLabelText,
61+
>findByLabelText : <T extends HTMLElement = HTMLElement>(container: HTMLElement, text: string) => ReturnType<FindByText<T>>
62+
> : ^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
63+
64+
};
65+
66+
type MapQueries<Q extends Queries = typeof queries> = {
67+
>MapQueries : MapQueries<Q>
68+
> : ^^^^^^^^^^^^^
69+
>queries : { findByLabelText: <T extends HTMLElement = HTMLElement>(container: HTMLElement, text: string) => ReturnType<FindByText<T>>; }
70+
> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^
71+
72+
[P in keyof Q]: Q[P];
73+
};
74+

0 commit comments

Comments
 (0)