Skip to content

Commit 3db0dc6

Browse files
committed
Just assign those contextual parameter types for now, without any attempt to hoist inferred type parameters
1 parent f6d53bd commit 3db0dc6

7 files changed

Lines changed: 537 additions & 154 deletions

src/compiler/checker.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38452,6 +38452,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3845238452
}
3845338453

3845438454
function inferFromAnnotatedParametersAndReturn(signature: Signature, context: Signature, inferenceContext: InferenceContext) {
38455+
if (inferenceContext.flags & InferenceFlags.IgnoreAnnotatedParametersAndReturns) {
38456+
return;
38457+
}
3845538458
const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
3845638459
for (let i = 0; i < len; i++) {
3845738460
const declaration = signature.parameters[i].valueDeclaration as ParameterDeclaration;
@@ -39460,9 +39463,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3946039463
if (!signature) {
3946139464
return;
3946239465
}
39466+
let saveInferenceFlags: InferenceFlags = InferenceFlags.None;
39467+
const inferenceContext = getInferenceContext(node);
39468+
if (inferenceContext) {
39469+
saveInferenceFlags = inferenceContext.flags;
39470+
// inferring from those could possibly infer inner type parameters into outer inference context
39471+
// and they could potentially leak in the final inferred type as `instantiateTypeWithSingleGenericCallSignature`
39472+
// can only "hoist" them using `context.inferredTypeParameters` in a narrow set of cases
39473+
inferenceContext.flags |= node.typeParameters ? InferenceFlags.IgnoreAnnotatedParametersAndReturns : InferenceFlags.None;
39474+
}
3946339475
if (isContextSensitive(node)) {
3946439476
if (contextualSignature) {
39465-
const inferenceContext = getInferenceContext(node);
3946639477
let instantiatedContextualSignature: Signature | undefined;
3946739478
if (checkMode && checkMode & CheckMode.Inferential) {
3946839479
inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!);
@@ -39481,7 +39492,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3948139492
}
3948239493
}
3948339494
else if (contextualSignature && !node.typeParameters && contextualSignature.parameters.length > node.parameters.length) {
39484-
const inferenceContext = getInferenceContext(node);
3948539495
if (checkMode && checkMode & CheckMode.Inferential) {
3948639496
inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!);
3948739497
}
@@ -39492,6 +39502,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3949239502
signature.resolvedReturnType = returnType;
3949339503
}
3949439504
}
39505+
if (inferenceContext) {
39506+
inferenceContext.flags = saveInferenceFlags;
39507+
}
3949539508
checkSignatureDeclaration(node);
3949639509
}
3949739510
}

src/compiler/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7129,6 +7129,7 @@ export const enum InferenceFlags {
71297129
NoDefault = 1 << 0, // Infer silentNeverType for no inferences (otherwise anyType or unknownType)
71307130
AnyDefault = 1 << 1, // Infer anyType (in JS files) for no inferences (otherwise unknownType)
71317131
SkippedGenericFunction = 1 << 2, // A generic function was skipped during inference
7132+
IgnoreAnnotatedParametersAndReturns = 1 << 3, // used when assigning contextual parameters types within generic functions
71327133
}
71337134

71347135
/**
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
contextualTypingGenericFunction2.ts(55,3): error TS2322: Type 'number' is not assignable to type 'boolean'.
2+
contextualTypingGenericFunction2.ts(65,3): error TS2322: Type '<N>(params: N) => (a: number, b: unknown) => boolean' is not assignable to type '(params: unknown) => (context: number, params: unknown) => number'.
3+
Call signature return types '(a: number, b: unknown) => boolean' and '(context: number, params: unknown) => number' are incompatible.
4+
Type 'boolean' is not assignable to type 'number'.
5+
6+
7+
==== contextualTypingGenericFunction2.ts (2 errors) ====
8+
// https://github.com/microsoft/TypeScript/issues/61791
9+
10+
declare const fn1: <T, Args extends Array<any>, Ret>(
11+
self: T,
12+
body: (this: T, ...args: Args) => Ret,
13+
) => (...args: Args) => Ret;
14+
15+
export const result1 = fn1({ message: "foo" }, function (n: number) {
16+
this.message;
17+
});
18+
19+
export const result2 = fn1({ message: "foo" }, function <N>(n: N) {
20+
this.message;
21+
});
22+
23+
declare const fn2: <Args extends Array<any>, Ret>(
24+
body: (first: string, ...args: Args) => Ret,
25+
) => (...args: Args) => Ret;
26+
27+
export const result3 = fn2(function <N>(first, n: N) {});
28+
29+
declare const fn3: <Args extends Array<any>, Ret>(
30+
body: (...args: Args) => (arg: string) => Ret,
31+
) => (...args: Args) => Ret;
32+
33+
export const result4 = fn3(function <N>(n: N) {
34+
return (arg) => {
35+
return 10
36+
}
37+
});
38+
39+
declare function fn4<T, P>(config: {
40+
context: T;
41+
callback: (params: P) => (context: T, params: P) => number;
42+
other?: (arg: string) => void;
43+
}): (params: P) => number;
44+
45+
export const result5 = fn4({
46+
context: 1,
47+
callback: <N,>(params: N) => {
48+
return (a, b) => a + 1;
49+
},
50+
});
51+
52+
export const result6 = fn4({
53+
context: 1,
54+
callback: <N,>(params: N) => {
55+
return (a, b) => a + 1;
56+
},
57+
other: (_) => {} // outer context-sensitive function
58+
});
59+
60+
// should error
61+
export const result7 = fn4({
62+
context: 1,
63+
~~~~~~~
64+
!!! error TS2322: Type 'number' is not assignable to type 'boolean'.
65+
!!! related TS6500 contextualTypingGenericFunction2.ts:33:3: The expected type comes from property 'context' which is declared here on type '{ context: boolean; callback: (params: unknown) => (context: boolean, params: unknown) => number; other?: ((arg: string) => void) | undefined; }'
66+
callback: <N,>(params: N) => {
67+
return (a: boolean, b) => a ? 1 : 2;
68+
},
69+
other: (_) => {} // outer context-sensitive function
70+
});
71+
72+
// should error
73+
export const result8 = fn4({
74+
context: 1,
75+
callback: <N,>(params: N) => {
76+
~~~~~~~~
77+
!!! error TS2322: Type '<N>(params: N) => (a: number, b: unknown) => boolean' is not assignable to type '(params: unknown) => (context: number, params: unknown) => number'.
78+
!!! error TS2322: Call signature return types '(a: number, b: unknown) => boolean' and '(context: number, params: unknown) => number' are incompatible.
79+
!!! error TS2322: Type 'boolean' is not assignable to type 'number'.
80+
!!! related TS6500 contextualTypingGenericFunction2.ts:34:3: The expected type comes from property 'callback' which is declared here on type '{ context: number; callback: (params: unknown) => (context: number, params: unknown) => number; other?: ((arg: string) => void) | undefined; }'
81+
return (a, b) => true;
82+
},
83+
other: (_) => {} // outer context-sensitive function
84+
});
85+
86+
declare const fnGen1: <T, Args extends Array<any>, Ret>(
87+
self: T,
88+
body: (this: T, ...args: Args) => Generator<any, Ret, never>,
89+
) => (...args: Args) => Ret;
90+
91+
export const result9 = fnGen1({ message: "foo" }, function* (n: number) {
92+
this.message;
93+
});
94+
95+
export const result10 = fnGen1({ message: "foo" }, function* <N>(n: N) {
96+
this.message;
97+
});
98+

tests/baselines/reference/contextualTypingGenericFunction2.js

Lines changed: 45 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@ declare const fn1: <T, Args extends Array<any>, Ret>(
88
body: (this: T, ...args: Args) => Ret,
99
) => (...args: Args) => Ret;
1010

11-
export const ok1 = fn1({ message: "foo" }, function (n: number) {
11+
export const result1 = fn1({ message: "foo" }, function (n: number) {
1212
this.message;
1313
});
1414

15-
export const ok2 = fn1({ message: "foo" }, function <N>(n: N) {
15+
export const result2 = fn1({ message: "foo" }, function <N>(n: N) {
1616
this.message;
1717
});
1818

1919
declare const fn2: <Args extends Array<any>, Ret>(
2020
body: (first: string, ...args: Args) => Ret,
2121
) => (...args: Args) => Ret;
2222

23-
export const ok3 = fn2(function <N>(first, n: N) {});
23+
export const result3 = fn2(function <N>(first, n: N) {});
2424

2525
declare const fn3: <Args extends Array<any>, Ret>(
2626
body: (...args: Args) => (arg: string) => Ret,
2727
) => (...args: Args) => Ret;
2828

29-
export const ok4 = fn3(function <N>(n: N) {
29+
export const result4 = fn3(function <N>(n: N) {
3030
return (arg) => {
3131
return 10
3232
}
@@ -35,55 +35,66 @@ export const ok4 = fn3(function <N>(n: N) {
3535
declare function fn4<T, P>(config: {
3636
context: T;
3737
callback: (params: P) => (context: T, params: P) => number;
38+
other?: (arg: string) => void;
3839
}): (params: P) => number;
3940

40-
export const ok5 = fn4({
41+
export const result5 = fn4({
4142
context: 1,
42-
callback: <T,>(params: T) => {
43+
callback: <N,>(params: N) => {
4344
return (a, b) => a + 1;
4445
},
4546
});
4647

48+
export const result6 = fn4({
49+
context: 1,
50+
callback: <N,>(params: N) => {
51+
return (a, b) => a + 1;
52+
},
53+
other: (_) => {} // outer context-sensitive function
54+
});
55+
56+
// should error
57+
export const result7 = fn4({
58+
context: 1,
59+
callback: <N,>(params: N) => {
60+
return (a: boolean, b) => a ? 1 : 2;
61+
},
62+
other: (_) => {} // outer context-sensitive function
63+
});
64+
65+
// should error
66+
export const result8 = fn4({
67+
context: 1,
68+
callback: <N,>(params: N) => {
69+
return (a, b) => true;
70+
},
71+
other: (_) => {} // outer context-sensitive function
72+
});
73+
4774
declare const fnGen1: <T, Args extends Array<any>, Ret>(
4875
self: T,
4976
body: (this: T, ...args: Args) => Generator<any, Ret, never>,
5077
) => (...args: Args) => Ret;
5178

52-
export const ok6 = fnGen1({ message: "foo" }, function* (n: number) {
79+
export const result9 = fnGen1({ message: "foo" }, function* (n: number) {
5380
this.message;
5481
});
5582

56-
export const ok7 = fnGen1({ message: "foo" }, function* <N>(n: N) {
83+
export const result10 = fnGen1({ message: "foo" }, function* <N>(n: N) {
5784
this.message;
5885
});
5986

6087

6188

6289

6390
//// [contextualTypingGenericFunction2.d.ts]
64-
export declare const ok1: (n: number) => void;
65-
export declare const ok2: (n: any) => void;
66-
export declare const ok3: <N>(n: N) => void;
67-
export declare const ok4: <N>(n: N) => number;
68-
export declare const ok5: (params: T) => number;
69-
export declare const ok6: (n: number) => void;
70-
export declare const ok7: (n: any) => void;
71-
72-
73-
//// [DtsFileErrors]
74-
75-
76-
contextualTypingGenericFunction2.d.ts(5,36): error TS2304: Cannot find name 'T'.
77-
78-
79-
==== contextualTypingGenericFunction2.d.ts (1 errors) ====
80-
export declare const ok1: (n: number) => void;
81-
export declare const ok2: (n: any) => void;
82-
export declare const ok3: <N>(n: N) => void;
83-
export declare const ok4: <N>(n: N) => number;
84-
export declare const ok5: (params: T) => number;
85-
~
86-
!!! error TS2304: Cannot find name 'T'.
87-
export declare const ok6: (n: number) => void;
88-
export declare const ok7: (n: any) => void;
89-
91+
export declare const result1: (n: number) => void;
92+
export declare const result2: (n: any) => void;
93+
export declare const result3: <N>(n: N) => void;
94+
export declare const result4: <N>(n: N) => number;
95+
export declare const result5: (params: unknown) => number;
96+
export declare const result6: (params: unknown) => number;
97+
export declare const result7: (params: unknown) => number;
98+
export declare const result8: (params: unknown) => number;
99+
export declare const result9: (n: number) => void;
100+
export declare const result10: (n: any) => void;

0 commit comments

Comments
 (0)