diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 80b61edd3657a..733155de2538b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -37949,7 +37949,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : fallbackType; } - function inferFromAnnotatedParameters(signature: Signature, context: Signature, inferenceContext: InferenceContext) { + function inferFromAnnotatedParametersAndReturn(signature: Signature, context: Signature, inferenceContext: InferenceContext) { const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0); for (let i = 0; i < len; i++) { const declaration = signature.parameters[i].valueDeclaration as ParameterDeclaration; @@ -37960,6 +37960,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { inferTypes(inferenceContext.inferences, source, target); } } + const typeNode = signature.declaration && getEffectiveReturnTypeNode(signature.declaration); + if (typeNode) { + const source = getTypeFromTypeNode(typeNode); + const target = getReturnTypeOfSignature(context); + inferTypes(inferenceContext.inferences, source, target); + } } function assignContextualParameterTypes(signature: Signature, context: Signature) { @@ -38957,7 +38963,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const inferenceContext = getInferenceContext(node); let instantiatedContextualSignature: Signature | undefined; if (checkMode && checkMode & CheckMode.Inferential) { - inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!); + inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!); const restType = getEffectiveRestType(contextualSignature); if (restType && restType.flags & TypeFlags.TypeParameter) { instantiatedContextualSignature = instantiateSignature(contextualSignature, inferenceContext!.nonFixingMapper); @@ -38975,7 +38981,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { else if (contextualSignature && !node.typeParameters && contextualSignature.parameters.length > node.parameters.length) { const inferenceContext = getInferenceContext(node); if (checkMode && checkMode & CheckMode.Inferential) { - inferFromAnnotatedParameters(signature, contextualSignature, inferenceContext!); + inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!); } } if (contextualSignature && !getReturnTypeFromAnnotation(node) && !signature.resolvedReturnType) { diff --git a/tests/baselines/reference/inferFromAnnotatedReturn1.errors.txt b/tests/baselines/reference/inferFromAnnotatedReturn1.errors.txt new file mode 100644 index 0000000000000..db802ab740742 --- /dev/null +++ b/tests/baselines/reference/inferFromAnnotatedReturn1.errors.txt @@ -0,0 +1,30 @@ +inferFromAnnotatedReturn1.ts(4,36): error TS2322: Type 'string' is not assignable to type 'number'. + + +==== inferFromAnnotatedReturn1.ts (1 errors) ==== + declare function test(cb: (arg: T) => T): T; + + const res1 = test((arg): number => 1); // ok + const res2 = test((arg): number => 'foo'); // error + ~~~~~ +!!! error TS2322: Type 'string' is not assignable to type 'number'. + + export declare function linkedSignal(options: { + source: () => S; + computation: (source: NoInfer) => D; + }): D; + + const signal = linkedSignal({ + source: () => 3, + computation: (s): number => 3, + }); + + class Foo { + constructor(readonly cb: (t: T, _: { x: number; other: NoInfer }) => R) {} + } + + const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({ + name, + x, + })); + \ No newline at end of file diff --git a/tests/baselines/reference/inferFromAnnotatedReturn1.symbols b/tests/baselines/reference/inferFromAnnotatedReturn1.symbols new file mode 100644 index 0000000000000..c6f99f61dd184 --- /dev/null +++ b/tests/baselines/reference/inferFromAnnotatedReturn1.symbols @@ -0,0 +1,88 @@ +//// [tests/cases/compiler/inferFromAnnotatedReturn1.ts] //// + +=== inferFromAnnotatedReturn1.ts === +declare function test(cb: (arg: T) => T): T; +>test : Symbol(test, Decl(inferFromAnnotatedReturn1.ts, 0, 0)) +>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22)) +>cb : Symbol(cb, Decl(inferFromAnnotatedReturn1.ts, 0, 25)) +>arg : Symbol(arg, Decl(inferFromAnnotatedReturn1.ts, 0, 30)) +>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22)) +>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22)) +>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 0, 22)) + +const res1 = test((arg): number => 1); // ok +>res1 : Symbol(res1, Decl(inferFromAnnotatedReturn1.ts, 2, 5)) +>test : Symbol(test, Decl(inferFromAnnotatedReturn1.ts, 0, 0)) +>arg : Symbol(arg, Decl(inferFromAnnotatedReturn1.ts, 2, 19)) + +const res2 = test((arg): number => 'foo'); // error +>res2 : Symbol(res2, Decl(inferFromAnnotatedReturn1.ts, 3, 5)) +>test : Symbol(test, Decl(inferFromAnnotatedReturn1.ts, 0, 0)) +>arg : Symbol(arg, Decl(inferFromAnnotatedReturn1.ts, 3, 19)) + +export declare function linkedSignal(options: { +>linkedSignal : Symbol(linkedSignal, Decl(inferFromAnnotatedReturn1.ts, 3, 42)) +>S : Symbol(S, Decl(inferFromAnnotatedReturn1.ts, 5, 37)) +>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39)) +>options : Symbol(options, Decl(inferFromAnnotatedReturn1.ts, 5, 43)) + + source: () => S; +>source : Symbol(source, Decl(inferFromAnnotatedReturn1.ts, 5, 53)) +>S : Symbol(S, Decl(inferFromAnnotatedReturn1.ts, 5, 37)) + + computation: (source: NoInfer) => D; +>computation : Symbol(computation, Decl(inferFromAnnotatedReturn1.ts, 6, 18)) +>source : Symbol(source, Decl(inferFromAnnotatedReturn1.ts, 7, 16)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39)) +>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39)) + +}): D; +>D : Symbol(D, Decl(inferFromAnnotatedReturn1.ts, 5, 39)) + +const signal = linkedSignal({ +>signal : Symbol(signal, Decl(inferFromAnnotatedReturn1.ts, 10, 5)) +>linkedSignal : Symbol(linkedSignal, Decl(inferFromAnnotatedReturn1.ts, 3, 42)) + + source: () => 3, +>source : Symbol(source, Decl(inferFromAnnotatedReturn1.ts, 10, 29)) + + computation: (s): number => 3, +>computation : Symbol(computation, Decl(inferFromAnnotatedReturn1.ts, 11, 18)) +>s : Symbol(s, Decl(inferFromAnnotatedReturn1.ts, 12, 16)) + +}); + +class Foo { +>Foo : Symbol(Foo, Decl(inferFromAnnotatedReturn1.ts, 13, 3)) +>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 15, 10)) +>R : Symbol(R, Decl(inferFromAnnotatedReturn1.ts, 15, 12)) + + constructor(readonly cb: (t: T, _: { x: number; other: NoInfer }) => R) {} +>cb : Symbol(Foo.cb, Decl(inferFromAnnotatedReturn1.ts, 16, 14)) +>t : Symbol(t, Decl(inferFromAnnotatedReturn1.ts, 16, 28)) +>T : Symbol(T, Decl(inferFromAnnotatedReturn1.ts, 15, 10)) +>_ : Symbol(_, Decl(inferFromAnnotatedReturn1.ts, 16, 33)) +>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 16, 38)) +>other : Symbol(other, Decl(inferFromAnnotatedReturn1.ts, 16, 49)) +>NoInfer : Symbol(NoInfer, Decl(lib.es5.d.ts, --, --)) +>R : Symbol(R, Decl(inferFromAnnotatedReturn1.ts, 15, 12)) +>R : Symbol(R, Decl(inferFromAnnotatedReturn1.ts, 15, 12)) +} + +const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({ +>_1 : Symbol(_1, Decl(inferFromAnnotatedReturn1.ts, 19, 5)) +>Foo : Symbol(Foo, Decl(inferFromAnnotatedReturn1.ts, 13, 3)) +>name : Symbol(name, Decl(inferFromAnnotatedReturn1.ts, 19, 20)) +>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 19, 35)) +>name : Symbol(name, Decl(inferFromAnnotatedReturn1.ts, 19, 43)) +>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 19, 57)) + + name, +>name : Symbol(name, Decl(inferFromAnnotatedReturn1.ts, 19, 75)) + + x, +>x : Symbol(x, Decl(inferFromAnnotatedReturn1.ts, 20, 7)) + +})); + diff --git a/tests/baselines/reference/inferFromAnnotatedReturn1.types b/tests/baselines/reference/inferFromAnnotatedReturn1.types new file mode 100644 index 0000000000000..a4b3c59878563 --- /dev/null +++ b/tests/baselines/reference/inferFromAnnotatedReturn1.types @@ -0,0 +1,136 @@ +//// [tests/cases/compiler/inferFromAnnotatedReturn1.ts] //// + +=== inferFromAnnotatedReturn1.ts === +declare function test(cb: (arg: T) => T): T; +>test : (cb: (arg: T) => T) => T +> : ^ ^^ ^^ ^^^^^ +>cb : (arg: T) => T +> : ^ ^^ ^^^^^ +>arg : T +> : ^ + +const res1 = test((arg): number => 1); // ok +>res1 : number +> : ^^^^^^ +>test((arg): number => 1) : number +> : ^^^^^^ +>test : (cb: (arg: T) => T) => T +> : ^ ^^ ^^ ^^^^^ +>(arg): number => 1 : (arg: number) => number +> : ^ ^^^^^^^^^^^^^ +>arg : number +> : ^^^^^^ +>1 : 1 +> : ^ + +const res2 = test((arg): number => 'foo'); // error +>res2 : number +> : ^^^^^^ +>test((arg): number => 'foo') : number +> : ^^^^^^ +>test : (cb: (arg: T) => T) => T +> : ^ ^^ ^^ ^^^^^ +>(arg): number => 'foo' : (arg: number) => number +> : ^ ^^^^^^^^^^^^^ +>arg : number +> : ^^^^^^ +>'foo' : "foo" +> : ^^^^^ + +export declare function linkedSignal(options: { +>linkedSignal : (options: { source: () => S; computation: (source: NoInfer) => D; }) => D +> : ^ ^^ ^^ ^^ ^^^^^ +>options : { source: () => S; computation: (source: NoInfer) => D; } +> : ^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^ + + source: () => S; +>source : () => S +> : ^^^^^^ + + computation: (source: NoInfer) => D; +>computation : (source: NoInfer) => D +> : ^ ^^ ^^^^^ +>source : NoInfer +> : ^^^^^^^^^^ + +}): D; + +const signal = linkedSignal({ +>signal : number +> : ^^^^^^ +>linkedSignal({ source: () => 3, computation: (s): number => 3,}) : number +> : ^^^^^^ +>linkedSignal : (options: { source: () => S; computation: (source: NoInfer) => D; }) => D +> : ^ ^^ ^^ ^^ ^^^^^ +>{ source: () => 3, computation: (s): number => 3,} : { source: () => number; computation: (s: number) => number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^ ^^^ + + source: () => 3, +>source : () => number +> : ^^^^^^^^^^^^ +>() => 3 : () => number +> : ^^^^^^^^^^^^ +>3 : 3 +> : ^ + + computation: (s): number => 3, +>computation : (s: number) => number +> : ^ ^^^^^^^^^^^^^ +>(s): number => 3 : (s: number) => number +> : ^ ^^^^^^^^^^^^^ +>s : number +> : ^^^^^^ +>3 : 3 +> : ^ + +}); + +class Foo { +>Foo : Foo +> : ^^^^^^^^^ + + constructor(readonly cb: (t: T, _: { x: number; other: NoInfer }) => R) {} +>cb : (t: T, _: { x: number; other: NoInfer; }) => R +> : ^ ^^ ^^ ^^ ^^^^^ +>t : T +> : ^ +>_ : { x: number; other: NoInfer; } +> : ^^^^^ ^^^^^^^^^ ^^^ +>x : number +> : ^^^^^^ +>other : NoInfer +> : ^^^^^^^^^^ +} + +const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({ +>_1 : Foo +> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^^ +>new Foo((name: string, { x }): { name: string; x: number } => ({ name, x,})) : Foo +> : ^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^^ +>Foo : typeof Foo +> : ^^^^^^^^^^ +>(name: string, { x }): { name: string; x: number } => ({ name, x,}) : (name: string, { x }: { x: number; other: NoInfer<{ name: string; x: number; }>; }) => { name: string; x: number; } +> : ^ ^^ ^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^ ^^^^^^^^^^^^ +>name : string +> : ^^^^^^ +>x : number +> : ^^^^^^ +>name : string +> : ^^^^^^ +>x : number +> : ^^^^^^ +>({ name, x,}) : { name: string; x: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>{ name, x,} : { name: string; x: number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + name, +>name : string +> : ^^^^^^ + + x, +>x : number +> : ^^^^^^ + +})); + diff --git a/tests/cases/compiler/inferFromAnnotatedReturn1.ts b/tests/cases/compiler/inferFromAnnotatedReturn1.ts new file mode 100644 index 0000000000000..c761f653c7ef7 --- /dev/null +++ b/tests/cases/compiler/inferFromAnnotatedReturn1.ts @@ -0,0 +1,26 @@ +// @strict: true +// @noEmit: true + +declare function test(cb: (arg: T) => T): T; + +const res1 = test((arg): number => 1); // ok +const res2 = test((arg): number => 'foo'); // error + +export declare function linkedSignal(options: { + source: () => S; + computation: (source: NoInfer) => D; +}): D; + +const signal = linkedSignal({ + source: () => 3, + computation: (s): number => 3, +}); + +class Foo { + constructor(readonly cb: (t: T, _: { x: number; other: NoInfer }) => R) {} +} + +const _1 = new Foo((name: string, { x }): { name: string; x: number } => ({ + name, + x, +}));