Skip to content

Commit 1583f7c

Browse files
committed
Infer type parameters only from applicable contextual types
1 parent 1ed0a5a commit 1583f7c

4 files changed

+103
-3
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31932,9 +31932,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3193231932
const contextualType = getContextualType(node, skipBindingPatterns ? ContextFlags.SkipBindingPatterns : ContextFlags.None);
3193331933
if (contextualType) {
3193431934
const inferenceTargetType = getReturnTypeOfSignature(signature);
31935+
const targetConstraint = getConstraintOfType(inferenceTargetType);
31936+
const applicableContextualType = targetConstraint && !isTypeAssignableTo(targetConstraint, contextualType) ?
31937+
filterType(contextualType, t => isTypeAssignableTo(t, targetConstraint)) :
31938+
contextualType;
3193531939
if (couldContainTypeVariables(inferenceTargetType)) {
3193631940
const outerContext = getInferenceContext(node);
31937-
const isFromBindingPattern = !skipBindingPatterns && getContextualType(node, ContextFlags.SkipBindingPatterns) !== contextualType;
31941+
const isFromBindingPattern = !skipBindingPatterns && getContextualType(node, ContextFlags.SkipBindingPatterns) !== applicableContextualType;
3193831942
// A return type inference from a binding pattern can be used in instantiating the contextual
3193931943
// type of an argument later in inference, but cannot stand on its own as the final return type.
3194031944
// It is incorporated into `context.returnMapper` which is used in `instantiateContextualType`,
@@ -31950,7 +31954,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3195031954
// outer call expression. Effectively we just want a snapshot of whatever has been
3195131955
// inferred for any outer call expression so far.
3195231956
const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
31953-
const instantiatedType = instantiateType(contextualType, outerMapper);
31957+
const instantiatedType = instantiateType(applicableContextualType, outerMapper);
3195431958
// If the contextual type is a generic function type with a single call signature, we
3195531959
// instantiate the type with its own type parameters and type arguments. This ensures that
3195631960
// the type parameters are not erased to type any during type inference such that they can
@@ -31970,7 +31974,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3197031974
// the source type uses the outer context's return mapper (which excludes inferences made from
3197131975
// outer arguments), and (b) we don't want any further inferences going into this context.
3197231976
const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
31973-
const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
31977+
const returnSourceType = instantiateType(applicableContextualType, outerContext && outerContext.returnMapper);
3197431978
inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
3197531979
context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined;
3197631980
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
=== tests/cases/compiler/inferTypeParameterFromApplicableContextualType.ts ===
2+
declare function useCallback<T extends Function>(fn: T): T;
3+
>useCallback : Symbol(useCallback, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 0))
4+
>T : Symbol(T, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 29))
5+
>Function : Symbol(Function, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
6+
>fn : Symbol(fn, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 49))
7+
>T : Symbol(T, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 29))
8+
>T : Symbol(T, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 29))
9+
10+
declare function ex1(callback: (x: number) => void): void;
11+
>ex1 : Symbol(ex1, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 59))
12+
>callback : Symbol(callback, Decl(inferTypeParameterFromApplicableContextualType.ts, 2, 21))
13+
>x : Symbol(x, Decl(inferTypeParameterFromApplicableContextualType.ts, 2, 32))
14+
15+
ex1(useCallback(x => {}));
16+
>ex1 : Symbol(ex1, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 59))
17+
>useCallback : Symbol(useCallback, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 0))
18+
>x : Symbol(x, Decl(inferTypeParameterFromApplicableContextualType.ts, 3, 16))
19+
20+
declare function ex2(callback?: (x: number) => void): void;
21+
>ex2 : Symbol(ex2, Decl(inferTypeParameterFromApplicableContextualType.ts, 3, 26))
22+
>callback : Symbol(callback, Decl(inferTypeParameterFromApplicableContextualType.ts, 5, 21))
23+
>x : Symbol(x, Decl(inferTypeParameterFromApplicableContextualType.ts, 5, 33))
24+
25+
ex2(useCallback(x => {}));
26+
>ex2 : Symbol(ex2, Decl(inferTypeParameterFromApplicableContextualType.ts, 3, 26))
27+
>useCallback : Symbol(useCallback, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 0))
28+
>x : Symbol(x, Decl(inferTypeParameterFromApplicableContextualType.ts, 6, 16))
29+
30+
declare function ex3(callback: ((x: number) => void) | 5): void;
31+
>ex3 : Symbol(ex3, Decl(inferTypeParameterFromApplicableContextualType.ts, 6, 26))
32+
>callback : Symbol(callback, Decl(inferTypeParameterFromApplicableContextualType.ts, 8, 21))
33+
>x : Symbol(x, Decl(inferTypeParameterFromApplicableContextualType.ts, 8, 33))
34+
35+
ex3(useCallback(x => {}));
36+
>ex3 : Symbol(ex3, Decl(inferTypeParameterFromApplicableContextualType.ts, 6, 26))
37+
>useCallback : Symbol(useCallback, Decl(inferTypeParameterFromApplicableContextualType.ts, 0, 0))
38+
>x : Symbol(x, Decl(inferTypeParameterFromApplicableContextualType.ts, 9, 16))
39+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/compiler/inferTypeParameterFromApplicableContextualType.ts ===
2+
declare function useCallback<T extends Function>(fn: T): T;
3+
>useCallback : <T extends Function>(fn: T) => T
4+
>fn : T
5+
6+
declare function ex1(callback: (x: number) => void): void;
7+
>ex1 : (callback: (x: number) => void) => void
8+
>callback : (x: number) => void
9+
>x : number
10+
11+
ex1(useCallback(x => {}));
12+
>ex1(useCallback(x => {})) : void
13+
>ex1 : (callback: (x: number) => void) => void
14+
>useCallback(x => {}) : (x: number) => void
15+
>useCallback : <T extends Function>(fn: T) => T
16+
>x => {} : (x: number) => void
17+
>x : number
18+
19+
declare function ex2(callback?: (x: number) => void): void;
20+
>ex2 : (callback?: ((x: number) => void) | undefined) => void
21+
>callback : ((x: number) => void) | undefined
22+
>x : number
23+
24+
ex2(useCallback(x => {}));
25+
>ex2(useCallback(x => {})) : void
26+
>ex2 : (callback?: ((x: number) => void) | undefined) => void
27+
>useCallback(x => {}) : (x: number) => void
28+
>useCallback : <T extends Function>(fn: T) => T
29+
>x => {} : (x: number) => void
30+
>x : number
31+
32+
declare function ex3(callback: ((x: number) => void) | 5): void;
33+
>ex3 : (callback: ((x: number) => void) | 5) => void
34+
>callback : ((x: number) => void) | 5
35+
>x : number
36+
37+
ex3(useCallback(x => {}));
38+
>ex3(useCallback(x => {})) : void
39+
>ex3 : (callback: ((x: number) => void) | 5) => void
40+
>useCallback(x => {}) : (x: number) => void
41+
>useCallback : <T extends Function>(fn: T) => T
42+
>x => {} : (x: number) => void
43+
>x : number
44+
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
declare function useCallback<T extends Function>(fn: T): T;
5+
6+
declare function ex1(callback: (x: number) => void): void;
7+
ex1(useCallback(x => {}));
8+
9+
declare function ex2(callback?: (x: number) => void): void;
10+
ex2(useCallback(x => {}));
11+
12+
declare function ex3(callback: ((x: number) => void) | 5): void;
13+
ex3(useCallback(x => {}));

0 commit comments

Comments
 (0)