Skip to content

Commit 34d5fbe

Browse files
committed
Filter return type inferences by constraint applicability
1 parent b1c52c5 commit 34d5fbe

File tree

4 files changed

+172
-3
lines changed

4 files changed

+172
-3
lines changed

src/compiler/checker.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26848,14 +26848,29 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2684826848
const constraint = getConstraintOfTypeParameter(inference.typeParameter);
2684926849
if (constraint) {
2685026850
const instantiatedConstraint = instantiateType(constraint, context.nonFixingMapper);
26851-
if (!inferredType || !context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
26852-
// If the fallback type satisfies the constraint, we pick it. Otherwise, we pick the constraint.
26853-
inference.inferredType = fallbackType && context.compareTypes(fallbackType, getTypeWithThisArgument(instantiatedConstraint, fallbackType)) ? fallbackType : instantiatedConstraint;
26851+
if (!inferredType) {
26852+
inference.inferredType = instantiatedConstraint;
26853+
} else {
26854+
if (inference.priority! & InferencePriority.ReturnType) {
26855+
inference.inferredType = filterContextualInferredType(inferredType, instantiatedConstraint) ?? (fallbackType && filterContextualInferredType(fallbackType, instantiatedConstraint)) ?? instantiatedConstraint;
26856+
} else if (!context.compareTypes(inferredType, getTypeWithThisArgument(instantiatedConstraint, inferredType))) {
26857+
// If the fallback type satisfies the constraint, we pick it. Otherwise, we pick the constraint.
26858+
inference.inferredType = fallbackType && context.compareTypes(fallbackType, getTypeWithThisArgument(instantiatedConstraint, fallbackType)) ? fallbackType : instantiatedConstraint;
26859+
}
2685426860
}
2685526861
}
2685626862
}
2685726863

2685826864
return inference.inferredType;
26865+
26866+
function filterContextualInferredType(inferredType: Type, constraint: Type) {
26867+
if (inferredType.flags & TypeFlags.Never) {
26868+
return inferredType;
26869+
}
26870+
const constraintWithThisArgument = getTypeWithThisArgument(constraint, inferredType);
26871+
const applicableByConstraint = filterType(inferredType, t => !!context.compareTypes(t, constraintWithThisArgument));
26872+
return !(applicableByConstraint.flags & TypeFlags.Never) ? inferredType : undefined;
26873+
}
2685926874
}
2686026875

2686126876
function getDefaultTypeArgumentType(isInJavaScriptFile: boolean): Type {
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, --, --))
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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/50719
5+
6+
declare function useCallback1<T extends Function>(fn: T): T;
7+
8+
declare function ex2(callback?: (x: number) => void): void;
9+
ex2(useCallback1((x) => {}));
10+
11+
declare function ex3(callback: ((x: number) => void) | 5): void;
12+
ex3(useCallback1((x) => {}));
13+
14+
// https://github.com/microsoft/TypeScript/issues/41461
15+
16+
declare function useCallback2<T extends (...args: any[]) => any>(
17+
callback: T,
18+
): T;
19+
const test: ((x: string) => void) | undefined = useCallback2((x) => {});

0 commit comments

Comments
 (0)