Skip to content

Commit 0d01692

Browse files
authored
Infer from annotated return type nodes before assigning contextual parameter types (#60964)
1 parent 12c2323 commit 0d01692

File tree

5 files changed

+289
-3
lines changed

5 files changed

+289
-3
lines changed

src/compiler/checker.ts

+9-3
Original file line numberDiff line numberDiff line change
@@ -37870,7 +37870,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3787037870
return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : fallbackType;
3787137871
}
3787237872

37873-
function inferFromAnnotatedParameters(signature: Signature, context: Signature, inferenceContext: InferenceContext) {
37873+
function inferFromAnnotatedParametersAndReturn(signature: Signature, context: Signature, inferenceContext: InferenceContext) {
3787437874
const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0);
3787537875
for (let i = 0; i < len; i++) {
3787637876
const declaration = signature.parameters[i].valueDeclaration as ParameterDeclaration;
@@ -37881,6 +37881,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3788137881
inferTypes(inferenceContext.inferences, source, target);
3788237882
}
3788337883
}
37884+
const typeNode = signature.declaration && getEffectiveReturnTypeNode(signature.declaration);
37885+
if (typeNode) {
37886+
const source = getTypeFromTypeNode(typeNode);
37887+
const target = getReturnTypeOfSignature(context);
37888+
inferTypes(inferenceContext.inferences, source, target);
37889+
}
3788437890
}
3788537891

3788637892
function assignContextualParameterTypes(signature: Signature, context: Signature) {
@@ -38878,7 +38884,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3887838884
const inferenceContext = getInferenceContext(node);
3887938885
let instantiatedContextualSignature: Signature | undefined;
3888038886
if (checkMode && checkMode & CheckMode.Inferential) {
38881-
inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!);
38887+
inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!);
3888238888
const restType = getEffectiveRestType(contextualSignature);
3888338889
if (restType && restType.flags & TypeFlags.TypeParameter) {
3888438890
instantiatedContextualSignature = instantiateSignature(contextualSignature, inferenceContext!.nonFixingMapper);
@@ -38896,7 +38902,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3889638902
else if (contextualSignature && !node.typeParameters && contextualSignature.parameters.length > node.parameters.length) {
3889738903
const inferenceContext = getInferenceContext(node);
3889838904
if (checkMode && checkMode & CheckMode.Inferential) {
38899-
inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!);
38905+
inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!);
3890038906
}
3890138907
}
3890238908
if (contextualSignature && !getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
inferFromAnnotatedReturn1.ts(4,36): error TS2322: Type 'string' is not assignable to type 'number'.
2+
3+
4+
==== inferFromAnnotatedReturn1.ts (1 errors) ====
5+
declare function test<T>(cb: (arg: T) => T): T;
6+
7+
const res1 = test((arg): number => 1); // ok
8+
const res2 = test((arg): number => 'foo'); // error
9+
~~~~~
10+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
11+
12+
export declare function linkedSignal<S, D>(options: {
13+
source: () => S;
14+
computation: (source: NoInfer<D>) => D;
15+
}): D;
16+
17+
const signal = linkedSignal({
18+
source: () => 3,
19+
computation: (s): number => 3,
20+
});
21+
22+
class Foo<T, R> {
23+
constructor(readonly cb: (t: T, _: { x: number; other: NoInfer<R> }) => R) {}
24+
}
25+
26+
const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({
27+
name,
28+
x,
29+
}));
30+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//// [tests/cases/compiler/inferFromAnnotatedReturn1.ts] ////
2+
3+
=== inferFromAnnotatedReturn1.ts ===
4+
declare function test<T>(cb: (arg: T) => T): T;
5+
>test : Symbol(test, Decl(inferFromAnnotatedReturn1.ts, 0, 0))
6+
>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22))
7+
>cb : Symbol(cb, Decl(inferFromAnnotatedReturn1.ts, 0, 25))
8+
>arg : Symbol(arg, Decl(inferFromAnnotatedReturn1.ts, 0, 30))
9+
>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22))
10+
>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22))
11+
>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22))
12+
13+
const res1 = test((arg): number => 1); // ok
14+
>res1 : Symbol(res1, Decl(inferFromAnnotatedReturn1.ts, 2, 5))
15+
>test : Symbol(test, Decl(inferFromAnnotatedReturn1.ts, 0, 0))
16+
>arg : Symbol(arg, Decl(inferFromAnnotatedReturn1.ts, 2, 19))
17+
18+
const res2 = test((arg): number => 'foo'); // error
19+
>res2 : Symbol(res2, Decl(inferFromAnnotatedReturn1.ts, 3, 5))
20+
>test : Symbol(test, Decl(inferFromAnnotatedReturn1.ts, 0, 0))
21+
>arg : Symbol(arg, Decl(inferFromAnnotatedReturn1.ts, 3, 19))
22+
23+
export declare function linkedSignal<S, D>(options: {
24+
>linkedSignal : Symbol(linkedSignal, Decl(inferFromAnnotatedReturn1.ts, 3, 42))
25+
>S : Symbol(S, Decl(inferFromAnnotatedReturn1.ts, 5, 37))
26+
>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39))
27+
>options : Symbol(options, Decl(inferFromAnnotatedReturn1.ts, 5, 43))
28+
29+
source: () => S;
30+
>source : Symbol(source, Decl(inferFromAnnotatedReturn1.ts, 5, 53))
31+
>S : Symbol(S, Decl(inferFromAnnotatedReturn1.ts, 5, 37))
32+
33+
computation: (source: NoInfer<D>) => D;
34+
>computation : Symbol(computation, Decl(inferFromAnnotatedReturn1.ts, 6, 18))
35+
>source : Symbol(source, Decl(inferFromAnnotatedReturn1.ts, 7, 16))
36+
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
37+
>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39))
38+
>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39))
39+
40+
}): D;
41+
>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39))
42+
43+
const signal = linkedSignal({
44+
>signal : Symbol(signal, Decl(inferFromAnnotatedReturn1.ts, 10, 5))
45+
>linkedSignal : Symbol(linkedSignal, Decl(inferFromAnnotatedReturn1.ts, 3, 42))
46+
47+
source: () => 3,
48+
>source : Symbol(source, Decl(inferFromAnnotatedReturn1.ts, 10, 29))
49+
50+
computation: (s): number => 3,
51+
>computation : Symbol(computation, Decl(inferFromAnnotatedReturn1.ts, 11, 18))
52+
>s : Symbol(s, Decl(inferFromAnnotatedReturn1.ts, 12, 16))
53+
54+
});
55+
56+
class Foo<T, R> {
57+
>Foo : Symbol(Foo, Decl(inferFromAnnotatedReturn1.ts, 13, 3))
58+
>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 15, 10))
59+
>R : Symbol(R, Decl(inferFromAnnotatedReturn1.ts, 15, 12))
60+
61+
constructor(readonly cb: (t: T, _: { x: number; other: NoInfer<R> }) => R) {}
62+
>cb : Symbol(Foo.cb, Decl(inferFromAnnotatedReturn1.ts, 16, 14))
63+
>t : Symbol(t, Decl(inferFromAnnotatedReturn1.ts, 16, 28))
64+
>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 15, 10))
65+
>_ : Symbol(_, Decl(inferFromAnnotatedReturn1.ts, 16, 33))
66+
>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 16, 38))
67+
>other : Symbol(other, Decl(inferFromAnnotatedReturn1.ts, 16, 49))
68+
>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --))
69+
>R : Symbol(R, Decl(inferFromAnnotatedReturn1.ts, 15, 12))
70+
>R : Symbol(R, Decl(inferFromAnnotatedReturn1.ts, 15, 12))
71+
}
72+
73+
const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({
74+
>_1 : Symbol(_1, Decl(inferFromAnnotatedReturn1.ts, 19, 5))
75+
>Foo : Symbol(Foo, Decl(inferFromAnnotatedReturn1.ts, 13, 3))
76+
>name : Symbol(name, Decl(inferFromAnnotatedReturn1.ts, 19, 20))
77+
>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 19, 35))
78+
>name : Symbol(name, Decl(inferFromAnnotatedReturn1.ts, 19, 43))
79+
>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 19, 57))
80+
81+
name,
82+
>name : Symbol(name, Decl(inferFromAnnotatedReturn1.ts, 19, 75))
83+
84+
x,
85+
>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 20, 7))
86+
87+
}));
88+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
//// [tests/cases/compiler/inferFromAnnotatedReturn1.ts] ////
2+
3+
=== inferFromAnnotatedReturn1.ts ===
4+
declare function test<T>(cb: (arg: T) => T): T;
5+
>test : <T>(cb: (arg: T) => T) => T
6+
> : ^ ^^ ^^ ^^^^^
7+
>cb : (arg: T) => T
8+
> : ^ ^^ ^^^^^
9+
>arg : T
10+
> : ^
11+
12+
const res1 = test((arg): number => 1); // ok
13+
>res1 : number
14+
> : ^^^^^^
15+
>test((arg): number => 1) : number
16+
> : ^^^^^^
17+
>test : <T>(cb: (arg: T) => T) => T
18+
> : ^ ^^ ^^ ^^^^^
19+
>(arg): number => 1 : (arg: number) => number
20+
> : ^ ^^^^^^^^^^^^^
21+
>arg : number
22+
> : ^^^^^^
23+
>1 : 1
24+
> : ^
25+
26+
const res2 = test((arg): number => 'foo'); // error
27+
>res2 : number
28+
> : ^^^^^^
29+
>test((arg): number => 'foo') : number
30+
> : ^^^^^^
31+
>test : <T>(cb: (arg: T) => T) => T
32+
> : ^ ^^ ^^ ^^^^^
33+
>(arg): number => 'foo' : (arg: number) => number
34+
> : ^ ^^^^^^^^^^^^^
35+
>arg : number
36+
> : ^^^^^^
37+
>'foo' : "foo"
38+
> : ^^^^^
39+
40+
export declare function linkedSignal<S, D>(options: {
41+
>linkedSignal : <S, D>(options: { source: () => S; computation: (source: NoInfer<D>) => D; }) => D
42+
> : ^ ^^ ^^ ^^ ^^^^^
43+
>options : { source: () => S; computation: (source: NoInfer<D>) => D; }
44+
> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^
45+
46+
source: () => S;
47+
>source : () => S
48+
> : ^^^^^^
49+
50+
computation: (source: NoInfer<D>) => D;
51+
>computation : (source: NoInfer<D>) => D
52+
> : ^ ^^ ^^^^^
53+
>source : NoInfer<D>
54+
> : ^^^^^^^^^^
55+
56+
}): D;
57+
58+
const signal = linkedSignal({
59+
>signal : number
60+
> : ^^^^^^
61+
>linkedSignal({ source: () => 3, computation: (s): number => 3,}) : number
62+
> : ^^^^^^
63+
>linkedSignal : <S, D>(options: { source: () => S; computation: (source: NoInfer<D>) => D; }) => D
64+
> : ^ ^^ ^^ ^^ ^^^^^
65+
>{ source: () => 3, computation: (s): number => 3,} : { source: () => number; computation: (s: number) => number; }
66+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^
67+
68+
source: () => 3,
69+
>source : () => number
70+
> : ^^^^^^^^^^^^
71+
>() => 3 : () => number
72+
> : ^^^^^^^^^^^^
73+
>3 : 3
74+
> : ^
75+
76+
computation: (s): number => 3,
77+
>computation : (s: number) => number
78+
> : ^ ^^^^^^^^^^^^^
79+
>(s): number => 3 : (s: number) => number
80+
> : ^ ^^^^^^^^^^^^^
81+
>s : number
82+
> : ^^^^^^
83+
>3 : 3
84+
> : ^
85+
86+
});
87+
88+
class Foo<T, R> {
89+
>Foo : Foo<T, R>
90+
> : ^^^^^^^^^
91+
92+
constructor(readonly cb: (t: T, _: { x: number; other: NoInfer<R> }) => R) {}
93+
>cb : (t: T, _: { x: number; other: NoInfer<R>; }) => R
94+
> : ^ ^^ ^^ ^^ ^^^^^
95+
>t : T
96+
> : ^
97+
>_ : { x: number; other: NoInfer<R>; }
98+
> : ^^^^^ ^^^^^^^^^ ^^^
99+
>x : number
100+
> : ^^^^^^
101+
>other : NoInfer<R>
102+
> : ^^^^^^^^^^
103+
}
104+
105+
const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({
106+
>_1 : Foo<string, { name: string; x: number; }>
107+
> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^^
108+
>new Foo((name: string, { x }): { name: string; x: number } => ({ name, x,})) : Foo<string, { name: string; x: number; }>
109+
> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^^
110+
>Foo : typeof Foo
111+
> : ^^^^^^^^^^
112+
>(name: string, { x }): { name: string; x: number } => ({ name, x,}) : (name: string, { x }: { x: number; other: NoInfer<{ name: string; x: number; }>; }) => { name: string; x: number; }
113+
> : ^ ^^ ^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^
114+
>name : string
115+
> : ^^^^^^
116+
>x : number
117+
> : ^^^^^^
118+
>name : string
119+
> : ^^^^^^
120+
>x : number
121+
> : ^^^^^^
122+
>({ name, x,}) : { name: string; x: number; }
123+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
124+
>{ name, x,} : { name: string; x: number; }
125+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
126+
127+
name,
128+
>name : string
129+
> : ^^^^^^
130+
131+
x,
132+
>x : number
133+
> : ^^^^^^
134+
135+
}));
136+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// @strict: true
2+
// @noEmit: true
3+
4+
declare function test<T>(cb: (arg: T) => T): T;
5+
6+
const res1 = test((arg): number => 1); // ok
7+
const res2 = test((arg): number => 'foo'); // error
8+
9+
export declare function linkedSignal<S, D>(options: {
10+
source: () => S;
11+
computation: (source: NoInfer<D>) => D;
12+
}): D;
13+
14+
const signal = linkedSignal({
15+
source: () => 3,
16+
computation: (s): number => 3,
17+
});
18+
19+
class Foo<T, R> {
20+
constructor(readonly cb: (t: T, _: { x: number; other: NoInfer<R> }) => R) {}
21+
}
22+
23+
const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({
24+
name,
25+
x,
26+
}));

0 commit comments

Comments
 (0)