From 94709a6548116002b003bfccb6fe0817d5eb243f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 1 Jun 2025 10:24:49 +0200 Subject: [PATCH 1/4] Assign contextual parameter types to functions with type parameters --- src/compiler/checker.ts | 2 +- src/compiler/utilities.ts | 21 +- .../contextualTypingGenericFunction2.js | 89 ++++++ .../contextualTypingGenericFunction2.symbols | 212 +++++++++++++ .../contextualTypingGenericFunction2.types | 296 ++++++++++++++++++ .../contextualTypingGenericFunction2.ts | 60 ++++ .../contextualTypingGenericFunction1.ts | 7 +- 7 files changed, 670 insertions(+), 17 deletions(-) create mode 100644 tests/baselines/reference/contextualTypingGenericFunction2.js create mode 100644 tests/baselines/reference/contextualTypingGenericFunction2.symbols create mode 100644 tests/baselines/reference/contextualTypingGenericFunction2.types create mode 100644 tests/cases/compiler/contextualTypingGenericFunction2.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a5c161abae051..f9350010c2a5e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21025,7 +21025,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) { - if (node.typeParameters || getEffectiveReturnTypeNode(node) || !node.body) { + if (getEffectiveReturnTypeNode(node) || !node.body) { return false; } if (node.body.kind !== SyntaxKind.Block) { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index ce3182ddd9d40..2729d846d974e 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10783,20 +10783,17 @@ export function getContainingNodeArray(node: Node): NodeArray | undefined /** @internal */ export function hasContextSensitiveParameters(node: FunctionLikeDeclaration): boolean { - // Functions with type parameters are not context sensitive. - if (!node.typeParameters) { - // Functions with any parameters that lack type annotations are context sensitive. - if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { + // Functions with any parameters that lack type annotations are context sensitive. + if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { + return true; + } + if (node.kind !== SyntaxKind.ArrowFunction) { + // If the first parameter is not an explicit 'this' parameter, then the function has + // an implicit 'this' parameter which is subject to contextual typing. + const parameter = firstOrUndefined(node.parameters); + if (!(parameter && parameterIsThisKeyword(parameter))) { return true; } - if (node.kind !== SyntaxKind.ArrowFunction) { - // If the first parameter is not an explicit 'this' parameter, then the function has - // an implicit 'this' parameter which is subject to contextual typing. - const parameter = firstOrUndefined(node.parameters); - if (!(parameter && parameterIsThisKeyword(parameter))) { - return true; - } - } } return false; } diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.js b/tests/baselines/reference/contextualTypingGenericFunction2.js new file mode 100644 index 0000000000000..5f06aa6fc7b8b --- /dev/null +++ b/tests/baselines/reference/contextualTypingGenericFunction2.js @@ -0,0 +1,89 @@ +//// [tests/cases/compiler/contextualTypingGenericFunction2.ts] //// + +//// [contextualTypingGenericFunction2.ts] +// https://github.com/microsoft/TypeScript/issues/61791 + +declare const fn1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Ret, +) => (...args: Args) => Ret; + +export const ok1 = fn1({ message: "foo" }, function (n: number) { + this.message; +}); + +export const ok2 = fn1({ message: "foo" }, function (n: N) { + this.message; +}); + +declare const fn2: , Ret>( + body: (first: string, ...args: Args) => Ret, +) => (...args: Args) => Ret; + +export const ok3 = fn2(function (first, n: N) {}); + +declare const fn3: , Ret>( + body: (...args: Args) => (arg: string) => Ret, +) => (...args: Args) => Ret; + +export const ok4 = fn3(function (n: N) { + return (arg) => { + return 10 + } +}); + +declare function fn4(config: { + context: T; + callback: (params: P) => (context: T, params: P) => number; +}): (params: P) => number; + +export const ok5 = fn4({ + context: 1, + callback: (params: T) => { + return (a, b) => a + 1; + }, +}); + +declare const fnGen1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Generator, +) => (...args: Args) => Ret; + +export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { + this.message; +}); + +export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { + this.message; +}); + + + + +//// [contextualTypingGenericFunction2.d.ts] +export declare const ok1: (n: number) => void; +export declare const ok2: (n: any) => void; +export declare const ok3: (n: N) => void; +export declare const ok4: (n: N) => number; +export declare const ok5: (params: T) => number; +export declare const ok6: (n: number) => void; +export declare const ok7: (n: any) => void; + + +//// [DtsFileErrors] + + +contextualTypingGenericFunction2.d.ts(5,36): error TS2304: Cannot find name 'T'. + + +==== contextualTypingGenericFunction2.d.ts (1 errors) ==== + export declare const ok1: (n: number) => void; + export declare const ok2: (n: any) => void; + export declare const ok3: (n: N) => void; + export declare const ok4: (n: N) => number; + export declare const ok5: (params: T) => number; + ~ +!!! error TS2304: Cannot find name 'T'. + export declare const ok6: (n: number) => void; + export declare const ok7: (n: any) => void; + \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.symbols b/tests/baselines/reference/contextualTypingGenericFunction2.symbols new file mode 100644 index 0000000000000..16c60357bd75f --- /dev/null +++ b/tests/baselines/reference/contextualTypingGenericFunction2.symbols @@ -0,0 +1,212 @@ +//// [tests/cases/compiler/contextualTypingGenericFunction2.ts] //// + +=== contextualTypingGenericFunction2.ts === +// https://github.com/microsoft/TypeScript/issues/61791 + +declare const fn1: , Ret>( +>fn1 : Symbol(fn1, Decl(contextualTypingGenericFunction2.ts, 2, 13)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 2, 20)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 2, 22)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 2, 47)) + + self: T, +>self : Symbol(self, Decl(contextualTypingGenericFunction2.ts, 2, 53)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 2, 20)) + + body: (this: T, ...args: Args) => Ret, +>body : Symbol(body, Decl(contextualTypingGenericFunction2.ts, 3, 10)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 4, 9)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 2, 20)) +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 4, 17)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 2, 22)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 2, 47)) + +) => (...args: Args) => Ret; +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 5, 6)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 2, 22)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 2, 47)) + +export const ok1 = fn1({ message: "foo" }, function (n: number) { +>ok1 : Symbol(ok1, Decl(contextualTypingGenericFunction2.ts, 7, 12)) +>fn1 : Symbol(fn1, Decl(contextualTypingGenericFunction2.ts, 2, 13)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 24)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 7, 53)) + + this.message; +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 24)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 4, 9)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 24)) + +}); + +export const ok2 = fn1({ message: "foo" }, function (n: N) { +>ok2 : Symbol(ok2, Decl(contextualTypingGenericFunction2.ts, 11, 12)) +>fn1 : Symbol(fn1, Decl(contextualTypingGenericFunction2.ts, 2, 13)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 24)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 11, 53)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 11, 56)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 11, 53)) + + this.message; +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 24)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 4, 9)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 24)) + +}); + +declare const fn2: , Ret>( +>fn2 : Symbol(fn2, Decl(contextualTypingGenericFunction2.ts, 15, 13)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 15, 20)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 15, 44)) + + body: (first: string, ...args: Args) => Ret, +>body : Symbol(body, Decl(contextualTypingGenericFunction2.ts, 15, 50)) +>first : Symbol(first, Decl(contextualTypingGenericFunction2.ts, 16, 9)) +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 16, 23)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 15, 20)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 15, 44)) + +) => (...args: Args) => Ret; +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 17, 6)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 15, 20)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 15, 44)) + +export const ok3 = fn2(function (first, n: N) {}); +>ok3 : Symbol(ok3, Decl(contextualTypingGenericFunction2.ts, 19, 12)) +>fn2 : Symbol(fn2, Decl(contextualTypingGenericFunction2.ts, 15, 13)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 19, 33)) +>first : Symbol(first, Decl(contextualTypingGenericFunction2.ts, 19, 36)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 19, 42)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 19, 33)) + +declare const fn3: , Ret>( +>fn3 : Symbol(fn3, Decl(contextualTypingGenericFunction2.ts, 21, 13)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 21, 20)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 21, 44)) + + body: (...args: Args) => (arg: string) => Ret, +>body : Symbol(body, Decl(contextualTypingGenericFunction2.ts, 21, 50)) +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 22, 9)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 21, 20)) +>arg : Symbol(arg, Decl(contextualTypingGenericFunction2.ts, 22, 28)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 21, 44)) + +) => (...args: Args) => Ret; +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 23, 6)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 21, 20)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 21, 44)) + +export const ok4 = fn3(function (n: N) { +>ok4 : Symbol(ok4, Decl(contextualTypingGenericFunction2.ts, 25, 12)) +>fn3 : Symbol(fn3, Decl(contextualTypingGenericFunction2.ts, 21, 13)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 25, 33)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 25, 36)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 25, 33)) + + return (arg) => { +>arg : Symbol(arg, Decl(contextualTypingGenericFunction2.ts, 26, 12)) + + return 10 + } +}); + +declare function fn4(config: { +>fn4 : Symbol(fn4, Decl(contextualTypingGenericFunction2.ts, 29, 3)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 31, 21)) +>P : Symbol(P, Decl(contextualTypingGenericFunction2.ts, 31, 23)) +>config : Symbol(config, Decl(contextualTypingGenericFunction2.ts, 31, 27)) + + context: T; +>context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 31, 36)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 31, 21)) + + callback: (params: P) => (context: T, params: P) => number; +>callback : Symbol(callback, Decl(contextualTypingGenericFunction2.ts, 32, 13)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 33, 13)) +>P : Symbol(P, Decl(contextualTypingGenericFunction2.ts, 31, 23)) +>context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 33, 28)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 31, 21)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 33, 39)) +>P : Symbol(P, Decl(contextualTypingGenericFunction2.ts, 31, 23)) + +}): (params: P) => number; +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 34, 5)) +>P : Symbol(P, Decl(contextualTypingGenericFunction2.ts, 31, 23)) + +export const ok5 = fn4({ +>ok5 : Symbol(ok5, Decl(contextualTypingGenericFunction2.ts, 36, 12)) +>fn4 : Symbol(fn4, Decl(contextualTypingGenericFunction2.ts, 29, 3)) + + context: 1, +>context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 36, 24)) + + callback: (params: T) => { +>callback : Symbol(callback, Decl(contextualTypingGenericFunction2.ts, 37, 13)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 38, 13)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 38, 17)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 38, 13)) + + return (a, b) => a + 1; +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 39, 12)) +>b : Symbol(b, Decl(contextualTypingGenericFunction2.ts, 39, 14)) +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 39, 12)) + + }, +}); + +declare const fnGen1: , Ret>( +>fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 43, 13)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 43, 23)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 43, 25)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 43, 50)) + + self: T, +>self : Symbol(self, Decl(contextualTypingGenericFunction2.ts, 43, 56)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 43, 23)) + + body: (this: T, ...args: Args) => Generator, +>body : Symbol(body, Decl(contextualTypingGenericFunction2.ts, 44, 10)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 45, 9)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 43, 23)) +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 45, 17)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 43, 25)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 43, 50)) + +) => (...args: Args) => Ret; +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 46, 6)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 43, 25)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 43, 50)) + +export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { +>ok6 : Symbol(ok6, Decl(contextualTypingGenericFunction2.ts, 48, 12)) +>fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 43, 13)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 48, 27)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 48, 57)) + + this.message; +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 48, 27)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 45, 9)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 48, 27)) + +}); + +export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { +>ok7 : Symbol(ok7, Decl(contextualTypingGenericFunction2.ts, 52, 12)) +>fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 43, 13)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 52, 27)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 52, 57)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 52, 60)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 52, 57)) + + this.message; +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 52, 27)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 45, 9)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 52, 27)) + +}); + diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.types b/tests/baselines/reference/contextualTypingGenericFunction2.types new file mode 100644 index 0000000000000..970aef92e6806 --- /dev/null +++ b/tests/baselines/reference/contextualTypingGenericFunction2.types @@ -0,0 +1,296 @@ +//// [tests/cases/compiler/contextualTypingGenericFunction2.ts] //// + +=== Performance Stats === +Type Count: 1,000 +Instantiation count: 2,500 + +=== contextualTypingGenericFunction2.ts === +// https://github.com/microsoft/TypeScript/issues/61791 + +declare const fn1: , Ret>( +>fn1 : , Ret>(self: T, body: (this: T, ...args: Args) => Ret) => (...args: Args) => Ret +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ + + self: T, +>self : T +> : ^ + + body: (this: T, ...args: Args) => Ret, +>body : (this: T, ...args: Args) => Ret +> : ^ ^^ ^^^^^ ^^ ^^^^^ +>this : T +> : ^ +>args : Args +> : ^^^^ + +) => (...args: Args) => Ret; +>args : Args +> : ^^^^ + +export const ok1 = fn1({ message: "foo" }, function (n: number) { +>ok1 : (n: number) => void +> : ^^^^^^^^^^^^^^^^^^^ +>fn1({ message: "foo" }, function (n: number) { this.message;}) : (n: number) => void +> : ^^^^^^^^^^^^^^^^^^^ +>fn1 : , Ret>(self: T, body: (this: T, ...args: Args) => Ret) => (...args: Args) => Ret +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ +>{ message: "foo" } : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ +>"foo" : "foo" +> : ^^^^^ +>function (n: number) { this.message;} : (this: { message: string; }, n: number) => void +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^ +>n : number +> : ^^^^^^ + + this.message; +>this.message : string +> : ^^^^^^ +>this : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ + +}); + +export const ok2 = fn1({ message: "foo" }, function (n: N) { +>ok2 : (n: any) => void +> : ^^^^^^^^^^^^^^^^ +>fn1({ message: "foo" }, function (n: N) { this.message;}) : (n: any) => void +> : ^^^^^^^^^^^^^^^^ +>fn1 : , Ret>(self: T, body: (this: T, ...args: Args) => Ret) => (...args: Args) => Ret +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ +>{ message: "foo" } : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ +>"foo" : "foo" +> : ^^^^^ +>function (n: N) { this.message;} : (this: { message: string; }, n: N) => void +> : ^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^ +>n : N +> : ^ + + this.message; +>this.message : string +> : ^^^^^^ +>this : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ + +}); + +declare const fn2: , Ret>( +>fn2 : , Ret>(body: (first: string, ...args: Args) => Ret) => (...args: Args) => Ret +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^^^^ + + body: (first: string, ...args: Args) => Ret, +>body : (first: string, ...args: Args) => Ret +> : ^ ^^ ^^^^^ ^^ ^^^^^ +>first : string +> : ^^^^^^ +>args : Args +> : ^^^^ + +) => (...args: Args) => Ret; +>args : Args +> : ^^^^ + +export const ok3 = fn2(function (first, n: N) {}); +>ok3 : (n: N) => void +> : ^ ^^^^^^^^^^^^^^^ +>fn2(function (first, n: N) {}) : (n: N) => void +> : ^ ^^^^^^^^^^^^^^^ +>fn2 : , Ret>(body: (first: string, ...args: Args) => Ret) => (...args: Args) => Ret +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^^^^ +>function (first, n: N) {} : (first: string, n: N) => void +> : ^ ^^ ^^^^^^^^^^ ^^ ^^^^^^^^^ +>first : string +> : ^^^^^^ +>n : N +> : ^ + +declare const fn3: , Ret>( +>fn3 : , Ret>(body: (...args: Args) => (arg: string) => Ret) => (...args: Args) => Ret +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^^^^ + + body: (...args: Args) => (arg: string) => Ret, +>body : (...args: Args) => (arg: string) => Ret +> : ^^^^ ^^ ^^^^^ +>args : Args +> : ^^^^ +>arg : string +> : ^^^^^^ + +) => (...args: Args) => Ret; +>args : Args +> : ^^^^ + +export const ok4 = fn3(function (n: N) { +>ok4 : (n: N) => number +> : ^ ^^^^^^^^^^^^^^^^^ +>fn3(function (n: N) { return (arg) => { return 10 }}) : (n: N) => number +> : ^ ^^^^^^^^^^^^^^^^^ +>fn3 : , Ret>(body: (...args: Args) => (arg: string) => Ret) => (...args: Args) => Ret +> : ^ ^^^^^^^^^ ^^ ^^ ^^ ^^^^^ +>function (n: N) { return (arg) => { return 10 }} : (n: N) => (arg: string) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^ +>n : N +> : ^ + + return (arg) => { +>(arg) => { return 10 } : (arg: string) => number +> : ^ ^^^^^^^^^^^^^^^^^^^ +>arg : string +> : ^^^^^^ + + return 10 +>10 : 10 +> : ^^ + } +}); + +declare function fn4(config: { +>fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; }) => (params: P) => number +> : ^ ^^ ^^ ^^ ^^^^^ +>config : { context: T; callback: (params: P) => (context: T, params: P) => number; } +> : ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^ + + context: T; +>context : T +> : ^ + + callback: (params: P) => (context: T, params: P) => number; +>callback : (params: P) => (context: T, params: P) => number +> : ^ ^^ ^^^^^ +>params : P +> : ^ +>context : T +> : ^ +>params : P +> : ^ + +}): (params: P) => number; +>params : P +> : ^ + +export const ok5 = fn4({ +>ok5 : (params: T) => number +> : ^ ^^^^^^^^ +>fn4({ context: 1, callback: (params: T) => { return (a, b) => a + 1; },}) : (params: T) => number +> : ^ ^^^^^^^^ +>fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; }) => (params: P) => number +> : ^ ^^ ^^ ^^ ^^^^^ +>{ context: 1, callback: (params: T) => { return (a, b) => a + 1; },} : { context: number; callback: (params: T) => (a: number, b: T) => number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ + + context: 1, +>context : number +> : ^^^^^^ +>1 : 1 +> : ^ + + callback: (params: T) => { +>callback : (params: T) => (a: number, b: T) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ +>(params: T) => { return (a, b) => a + 1; } : (params: T) => (a: number, b: T) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ +>params : T +> : ^ + + return (a, b) => a + 1; +>(a, b) => a + 1 : (a: number, b: T) => number +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>b : T +> : ^ +>a + 1 : number +> : ^^^^^^ +>a : number +> : ^^^^^^ +>1 : 1 +> : ^ + + }, +}); + +declare const fnGen1: , Ret>( +>fnGen1 : , Ret>(self: T, body: (this: T, ...args: Args) => Generator) => (...args: Args) => Ret +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ + + self: T, +>self : T +> : ^ + + body: (this: T, ...args: Args) => Generator, +>body : (this: T, ...args: Args) => Generator +> : ^ ^^ ^^^^^ ^^ ^^^^^ +>this : T +> : ^ +>args : Args +> : ^^^^ + +) => (...args: Args) => Ret; +>args : Args +> : ^^^^ + +export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { +>ok6 : (n: number) => void +> : ^^^^^^^^^^^^^^^^^^^ +>fnGen1({ message: "foo" }, function* (n: number) { this.message;}) : (n: number) => void +> : ^^^^^^^^^^^^^^^^^^^ +>fnGen1 : , Ret>(self: T, body: (this: T, ...args: Args) => Generator) => (...args: Args) => Ret +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ +>{ message: "foo" } : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ +>"foo" : "foo" +> : ^^^^^ +>function* (n: number) { this.message;} : (this: { message: string; }, n: number) => Generator +> : ^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>n : number +> : ^^^^^^ + + this.message; +>this.message : string +> : ^^^^^^ +>this : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ + +}); + +export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { +>ok7 : (n: any) => void +> : ^^^^^^^^^^^^^^^^ +>fnGen1({ message: "foo" }, function* (n: N) { this.message;}) : (n: any) => void +> : ^^^^^^^^^^^^^^^^ +>fnGen1 : , Ret>(self: T, body: (this: T, ...args: Args) => Generator) => (...args: Args) => Ret +> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ +>{ message: "foo" } : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ +>"foo" : "foo" +> : ^^^^^ +>function* (n: N) { this.message;} : (this: { message: string; }, n: N) => Generator +> : ^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>n : N +> : ^ + + this.message; +>this.message : string +> : ^^^^^^ +>this : { message: string; } +> : ^^^^^^^^^^^^^^^^^^^^ +>message : string +> : ^^^^^^ + +}); + diff --git a/tests/cases/compiler/contextualTypingGenericFunction2.ts b/tests/cases/compiler/contextualTypingGenericFunction2.ts new file mode 100644 index 0000000000000..13a4e42c9baeb --- /dev/null +++ b/tests/cases/compiler/contextualTypingGenericFunction2.ts @@ -0,0 +1,60 @@ +// @strict: true +// @target: esnext +// @declaration: true +// @emitDeclarationOnly: true + +// https://github.com/microsoft/TypeScript/issues/61791 + +declare const fn1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Ret, +) => (...args: Args) => Ret; + +export const ok1 = fn1({ message: "foo" }, function (n: number) { + this.message; +}); + +export const ok2 = fn1({ message: "foo" }, function (n: N) { + this.message; +}); + +declare const fn2: , Ret>( + body: (first: string, ...args: Args) => Ret, +) => (...args: Args) => Ret; + +export const ok3 = fn2(function (first, n: N) {}); + +declare const fn3: , Ret>( + body: (...args: Args) => (arg: string) => Ret, +) => (...args: Args) => Ret; + +export const ok4 = fn3(function (n: N) { + return (arg) => { + return 10 + } +}); + +declare function fn4(config: { + context: T; + callback: (params: P) => (context: T, params: P) => number; +}): (params: P) => number; + +export const ok5 = fn4({ + context: 1, + callback: (params: T) => { + return (a, b) => a + 1; + }, +}); + +declare const fnGen1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Generator, +) => (...args: Args) => Ret; + +export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { + this.message; +}); + +export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { + this.message; +}); diff --git a/tests/cases/fourslash/contextualTypingGenericFunction1.ts b/tests/cases/fourslash/contextualTypingGenericFunction1.ts index 9fa6b92da237a..31411f72609c9 100644 --- a/tests/cases/fourslash/contextualTypingGenericFunction1.ts +++ b/tests/cases/fourslash/contextualTypingGenericFunction1.ts @@ -1,6 +1,5 @@ /// -// should not contextually type the RHS because it introduces type parameters ////var obj: { f(x: T): T } = { f: (/*1*/x) => x }; ////var obj2: (x: T) => T = (/*2*/x) => x; //// @@ -11,7 +10,7 @@ ////c.obj = (/*3*/x) => x; verify.quickInfos({ - 1: "(parameter) x: any", - 2: "(parameter) x: any", - 3: "(parameter) x: any" + 1: "(parameter) x: T", + 2: "(parameter) x: T", + 3: "(parameter) x: T" }); From b97406f91cd60a695e7191f0b13cb58fec241386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Thu, 26 Jun 2025 14:09:43 +0200 Subject: [PATCH 2/4] assign and resolve some contextual parameter types late --- src/compiler/checker.ts | 25 ++++++-- src/compiler/utilities.ts | 21 ++++--- ...ontextualTypingGenericFunction2.errors.txt | 62 +++++++++++++++++++ .../contextualTypingGenericFunction2.js | 25 +------- .../contextualTypingGenericFunction2.types | 56 ++++++++--------- tests/baselines/reference/literalTypes2.types | 6 +- .../contextualTypingGenericFunction1.ts | 7 ++- 7 files changed, 133 insertions(+), 69 deletions(-) create mode 100644 tests/baselines/reference/contextualTypingGenericFunction2.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 571a75daa16bc..a7e7319f8e59a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21088,7 +21088,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) { - if (getEffectiveReturnTypeNode(node) || !node.body) { + if (node.typeParameters || getEffectiveReturnTypeNode(node) || !node.body) { return false; } if (node.body.kind !== SyntaxKind.Block) { @@ -26067,7 +26067,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { }); } - function applyToParameterTypes(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) { + function applyToParameterTypes(source: Signature, target: Signature, callback: (s: Type, t: Type) => void, skipUnannotatedParameters = false) { + const sourceDeclaredCount = source.parameters.length - (signatureHasRestParameter(source) ? 1 : 0); const sourceCount = getParameterCount(source); const targetCount = getParameterCount(target); const sourceRestType = getEffectiveRestType(source); @@ -26082,6 +26083,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } for (let i = 0; i < paramCount; i++) { + if (skipUnannotatedParameters) { + const decl = i < sourceDeclaredCount ? source.parameters[i] : signatureHasRestParameter(source) ? source.parameters[sourceDeclaredCount] : undefined; + if (decl?.valueDeclaration && !getEffectiveTypeAnnotationNode(decl.valueDeclaration)) { + continue + } + } callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } if (targetRestType) { @@ -41376,7 +41383,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const inferences = map(context.inferences, info => createInferenceInfo(info.typeParameter)); applyToParameterTypes(instantiatedSignature, contextualSignature, (source, target) => { inferTypes(inferences, source, target, /*priority*/ 0, /*contravariant*/ true); - }); + }, /*skipUnannotatedParameters*/ true); if (some(inferences, hasInferenceCandidates)) { // We have inference candidates, indicating that one or more type parameters are referenced // in the parameter types of the contextual signature. Now also infer from the return type. @@ -41389,6 +41396,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!hasOverlappingInferences(context.inferences, inferences)) { mergeInferences(context.inferences, inferences); context.inferredTypeParameters = concatenate(context.inferredTypeParameters, uniqueTypeParameters); + assignContextualParameterTypes(signature, instantiateSignature(contextualSignature, context.mapper)); return getOrCreateTypeFromSignature(instantiatedSignature); } } @@ -41812,7 +41820,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // or if its FunctionBody is strict code(11.1.5). checkGrammarModifiers(node); - checkVariableLikeDeclaration(node); + if (getEffectiveTypeAnnotationNode(node)) { + // checking annotated parameters early allows the compiler to find circularties early + checkVariableLikeDeclaration(node); + } else { + // defer resolving the type of unannotated parameters so that late contextual parameter types can be assigned before it + checkNodeDeferred(node); + } const func = getContainingFunction(node)!; if (hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) { if (compilerOptions.erasableSyntaxOnly) { @@ -49158,6 +49172,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ClassExpression: checkClassExpressionDeferred(node as ClassExpression); break; + case SyntaxKind.Parameter: + checkVariableLikeDeclaration(node as ParameterDeclaration); + break; case SyntaxKind.TypeParameter: checkTypeParameterDeferred(node as TypeParameterDeclaration); break; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 7a49ea2f842e5..f5b785dc5db8f 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10814,17 +10814,20 @@ export function getContainingNodeArray(node: Node): NodeArray | undefined /** @internal */ export function hasContextSensitiveParameters(node: FunctionLikeDeclaration): boolean { - // Functions with any parameters that lack type annotations are context sensitive. - if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { - return true; - } - if (node.kind !== SyntaxKind.ArrowFunction) { - // If the first parameter is not an explicit 'this' parameter, then the function has - // an implicit 'this' parameter which is subject to contextual typing. - const parameter = firstOrUndefined(node.parameters); - if (!(parameter && parameterIsThisKeyword(parameter))) { + // Functions with type parameters are not context sensitive. + if (!node.typeParameters) { + // Functions with any parameters that lack type annotations are context sensitive. + if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { return true; } + if (node.kind !== SyntaxKind.ArrowFunction) { + // If the first parameter is not an explicit 'this' parameter, then the function has + // an implicit 'this' parameter which is subject to contextual typing. + const parameter = firstOrUndefined(node.parameters); + if (!(parameter && parameterIsThisKeyword(parameter))) { + return true; + } + } } return false; } diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt b/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt new file mode 100644 index 0000000000000..fcacb3c60dfc5 --- /dev/null +++ b/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt @@ -0,0 +1,62 @@ +contextualTypingGenericFunction2.ts(40,22): error TS18046: 'a' is of type 'unknown'. + + +==== contextualTypingGenericFunction2.ts (1 errors) ==== + // https://github.com/microsoft/TypeScript/issues/61791 + + declare const fn1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Ret, + ) => (...args: Args) => Ret; + + export const ok1 = fn1({ message: "foo" }, function (n: number) { + this.message; + }); + + export const ok2 = fn1({ message: "foo" }, function (n: N) { + this.message; + }); + + declare const fn2: , Ret>( + body: (first: string, ...args: Args) => Ret, + ) => (...args: Args) => Ret; + + export const ok3 = fn2(function (first, n: N) {}); + + declare const fn3: , Ret>( + body: (...args: Args) => (arg: string) => Ret, + ) => (...args: Args) => Ret; + + export const ok4 = fn3(function (n: N) { + return (arg) => { + return 10 + } + }); + + declare function fn4(config: { + context: T; + callback: (params: P) => (context: T, params: P) => number; + }): (params: P) => number; + + export const ok5 = fn4({ + context: 1, + callback: (params: T) => { + return (a, b) => a + 1; + ~ +!!! error TS18046: 'a' is of type 'unknown'. + }, + }); + + declare const fnGen1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Generator, + ) => (...args: Args) => Ret; + + export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { + this.message; + }); + + export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { + this.message; + }); + \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.js b/tests/baselines/reference/contextualTypingGenericFunction2.js index 5f06aa6fc7b8b..712b4155fae22 100644 --- a/tests/baselines/reference/contextualTypingGenericFunction2.js +++ b/tests/baselines/reference/contextualTypingGenericFunction2.js @@ -62,28 +62,9 @@ export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { //// [contextualTypingGenericFunction2.d.ts] export declare const ok1: (n: number) => void; -export declare const ok2: (n: any) => void; +export declare const ok2: (n: N) => void; export declare const ok3: (n: N) => void; export declare const ok4: (n: N) => number; -export declare const ok5: (params: T) => number; +export declare const ok5: (params: T) => number; export declare const ok6: (n: number) => void; -export declare const ok7: (n: any) => void; - - -//// [DtsFileErrors] - - -contextualTypingGenericFunction2.d.ts(5,36): error TS2304: Cannot find name 'T'. - - -==== contextualTypingGenericFunction2.d.ts (1 errors) ==== - export declare const ok1: (n: number) => void; - export declare const ok2: (n: any) => void; - export declare const ok3: (n: N) => void; - export declare const ok4: (n: N) => number; - export declare const ok5: (params: T) => number; - ~ -!!! error TS2304: Cannot find name 'T'. - export declare const ok6: (n: number) => void; - export declare const ok7: (n: any) => void; - \ No newline at end of file +export declare const ok7: (n: N) => void; diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.types b/tests/baselines/reference/contextualTypingGenericFunction2.types index 970aef92e6806..6ec974c024a79 100644 --- a/tests/baselines/reference/contextualTypingGenericFunction2.types +++ b/tests/baselines/reference/contextualTypingGenericFunction2.types @@ -56,10 +56,10 @@ export const ok1 = fn1({ message: "foo" }, function (n: number) { }); export const ok2 = fn1({ message: "foo" }, function (n: N) { ->ok2 : (n: any) => void -> : ^^^^^^^^^^^^^^^^ ->fn1({ message: "foo" }, function (n: N) { this.message;}) : (n: any) => void -> : ^^^^^^^^^^^^^^^^ +>ok2 : (n: N) => void +> : ^ ^^^^^^^^^^^^^^^ +>fn1({ message: "foo" }, function (n: N) { this.message;}) : (n: N) => void +> : ^ ^^^^^^^^^^^^^^^ >fn1 : , Ret>(self: T, body: (this: T, ...args: Args) => Ret) => (...args: Args) => Ret > : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ >{ message: "foo" } : { message: string; } @@ -178,14 +178,14 @@ declare function fn4(config: { > : ^ export const ok5 = fn4({ ->ok5 : (params: T) => number -> : ^ ^^^^^^^^ ->fn4({ context: 1, callback: (params: T) => { return (a, b) => a + 1; },}) : (params: T) => number -> : ^ ^^^^^^^^ +>ok5 : (params: T) => number +> : ^ ^^ ^^^^^^^^ +>fn4({ context: 1, callback: (params: T) => { return (a, b) => a + 1; },}) : (params: T) => number +> : ^ ^^ ^^^^^^^^ >fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; }) => (params: P) => number > : ^ ^^ ^^ ^^ ^^^^^ ->{ context: 1, callback: (params: T) => { return (a, b) => a + 1; },} : { context: number; callback: (params: T) => (a: number, b: T) => number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +>{ context: 1, callback: (params: T) => { return (a, b) => a + 1; },} : { context: number; callback: (params: T) => (a: unknown, b: unknown) => any; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ context: 1, >context : number @@ -194,24 +194,24 @@ export const ok5 = fn4({ > : ^ callback: (params: T) => { ->callback : (params: T) => (a: number, b: T) => number -> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ ->(params: T) => { return (a, b) => a + 1; } : (params: T) => (a: number, b: T) => number -> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ +>callback : (params: T) => (a: unknown, b: unknown) => any +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +>(params: T) => { return (a, b) => a + 1; } : (params: T) => (a: unknown, b: unknown) => any +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ >params : T > : ^ return (a, b) => a + 1; ->(a, b) => a + 1 : (a: number, b: T) => number -> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ ->a : number -> : ^^^^^^ ->b : T -> : ^ ->a + 1 : number -> : ^^^^^^ ->a : number -> : ^^^^^^ +>(a, b) => a + 1 : (a: unknown, b: unknown) => any +> : ^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +>a : unknown +> : ^^^^^^^ +>b : unknown +> : ^^^^^^^ +>a + 1 : any +> : ^^^ +>a : unknown +> : ^^^^^^^ >1 : 1 > : ^ @@ -267,10 +267,10 @@ export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { }); export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { ->ok7 : (n: any) => void -> : ^^^^^^^^^^^^^^^^ ->fnGen1({ message: "foo" }, function* (n: N) { this.message;}) : (n: any) => void -> : ^^^^^^^^^^^^^^^^ +>ok7 : (n: N) => void +> : ^ ^^^^^^^^^^^^^^^ +>fnGen1({ message: "foo" }, function* (n: N) { this.message;}) : (n: N) => void +> : ^ ^^^^^^^^^^^^^^^ >fnGen1 : , Ret>(self: T, body: (this: T, ...args: Args) => Generator) => (...args: Args) => Ret > : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ >{ message: "foo" } : { message: string; } diff --git a/tests/baselines/reference/literalTypes2.types b/tests/baselines/reference/literalTypes2.types index 2744be4c5f10b..8190a61a91afd 100644 --- a/tests/baselines/reference/literalTypes2.types +++ b/tests/baselines/reference/literalTypes2.types @@ -375,9 +375,9 @@ function f3() { > : ^^^^^ const c3 = cond ? E.A : cond ? true : 123; ->c3 : true | E.A | 123 +>c3 : true | 123 | E.A > : ^^^^^^^^^^^^^^^^ ->cond ? E.A : cond ? true : 123 : true | E.A | 123 +>cond ? E.A : cond ? true : 123 : true | 123 | E.A > : ^^^^^^^^^^^^^^^^ >cond : boolean > : ^^^^^^^ @@ -473,7 +473,7 @@ function f3() { let x3 = c3; >x3 : number | boolean > : ^^^^^^^^^^^^^^^^ ->c3 : true | E.A | 123 +>c3 : true | 123 | E.A > : ^^^^^^^^^^^^^^^^ let x4 = c4; diff --git a/tests/cases/fourslash/contextualTypingGenericFunction1.ts b/tests/cases/fourslash/contextualTypingGenericFunction1.ts index 31411f72609c9..9fa6b92da237a 100644 --- a/tests/cases/fourslash/contextualTypingGenericFunction1.ts +++ b/tests/cases/fourslash/contextualTypingGenericFunction1.ts @@ -1,5 +1,6 @@ /// +// should not contextually type the RHS because it introduces type parameters ////var obj: { f(x: T): T } = { f: (/*1*/x) => x }; ////var obj2: (x: T) => T = (/*2*/x) => x; //// @@ -10,7 +11,7 @@ ////c.obj = (/*3*/x) => x; verify.quickInfos({ - 1: "(parameter) x: T", - 2: "(parameter) x: T", - 3: "(parameter) x: T" + 1: "(parameter) x: any", + 2: "(parameter) x: any", + 3: "(parameter) x: any" }); From f6d53bd54b09d54a6278a3e60c9da60db8d7ab09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 30 Jun 2025 10:10:43 +0200 Subject: [PATCH 3/4] Revert "assign and resolve some contextual parameter types late" This reverts commit b97406f91cd60a695e7191f0b13cb58fec241386. --- src/compiler/checker.ts | 25 ++------ src/compiler/utilities.ts | 21 +++---- ...ontextualTypingGenericFunction2.errors.txt | 62 ------------------- .../contextualTypingGenericFunction2.js | 25 +++++++- .../contextualTypingGenericFunction2.types | 56 ++++++++--------- tests/baselines/reference/literalTypes2.types | 6 +- .../contextualTypingGenericFunction1.ts | 7 +-- 7 files changed, 69 insertions(+), 133 deletions(-) delete mode 100644 tests/baselines/reference/contextualTypingGenericFunction2.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a7e7319f8e59a..571a75daa16bc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21088,7 +21088,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function hasContextSensitiveReturnExpression(node: FunctionLikeDeclaration) { - if (node.typeParameters || getEffectiveReturnTypeNode(node) || !node.body) { + if (getEffectiveReturnTypeNode(node) || !node.body) { return false; } if (node.body.kind !== SyntaxKind.Block) { @@ -26067,8 +26067,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { }); } - function applyToParameterTypes(source: Signature, target: Signature, callback: (s: Type, t: Type) => void, skipUnannotatedParameters = false) { - const sourceDeclaredCount = source.parameters.length - (signatureHasRestParameter(source) ? 1 : 0); + function applyToParameterTypes(source: Signature, target: Signature, callback: (s: Type, t: Type) => void) { const sourceCount = getParameterCount(source); const targetCount = getParameterCount(target); const sourceRestType = getEffectiveRestType(source); @@ -26083,12 +26082,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } for (let i = 0; i < paramCount; i++) { - if (skipUnannotatedParameters) { - const decl = i < sourceDeclaredCount ? source.parameters[i] : signatureHasRestParameter(source) ? source.parameters[sourceDeclaredCount] : undefined; - if (decl?.valueDeclaration && !getEffectiveTypeAnnotationNode(decl.valueDeclaration)) { - continue - } - } callback(getTypeAtPosition(source, i), getTypeAtPosition(target, i)); } if (targetRestType) { @@ -41383,7 +41376,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const inferences = map(context.inferences, info => createInferenceInfo(info.typeParameter)); applyToParameterTypes(instantiatedSignature, contextualSignature, (source, target) => { inferTypes(inferences, source, target, /*priority*/ 0, /*contravariant*/ true); - }, /*skipUnannotatedParameters*/ true); + }); if (some(inferences, hasInferenceCandidates)) { // We have inference candidates, indicating that one or more type parameters are referenced // in the parameter types of the contextual signature. Now also infer from the return type. @@ -41396,7 +41389,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!hasOverlappingInferences(context.inferences, inferences)) { mergeInferences(context.inferences, inferences); context.inferredTypeParameters = concatenate(context.inferredTypeParameters, uniqueTypeParameters); - assignContextualParameterTypes(signature, instantiateSignature(contextualSignature, context.mapper)); return getOrCreateTypeFromSignature(instantiatedSignature); } } @@ -41820,13 +41812,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // or if its FunctionBody is strict code(11.1.5). checkGrammarModifiers(node); - if (getEffectiveTypeAnnotationNode(node)) { - // checking annotated parameters early allows the compiler to find circularties early - checkVariableLikeDeclaration(node); - } else { - // defer resolving the type of unannotated parameters so that late contextual parameter types can be assigned before it - checkNodeDeferred(node); - } + checkVariableLikeDeclaration(node); const func = getContainingFunction(node)!; if (hasSyntacticModifier(node, ModifierFlags.ParameterPropertyModifier)) { if (compilerOptions.erasableSyntaxOnly) { @@ -49172,9 +49158,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.ClassExpression: checkClassExpressionDeferred(node as ClassExpression); break; - case SyntaxKind.Parameter: - checkVariableLikeDeclaration(node as ParameterDeclaration); - break; case SyntaxKind.TypeParameter: checkTypeParameterDeferred(node as TypeParameterDeclaration); break; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index f5b785dc5db8f..7a49ea2f842e5 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -10814,20 +10814,17 @@ export function getContainingNodeArray(node: Node): NodeArray | undefined /** @internal */ export function hasContextSensitiveParameters(node: FunctionLikeDeclaration): boolean { - // Functions with type parameters are not context sensitive. - if (!node.typeParameters) { - // Functions with any parameters that lack type annotations are context sensitive. - if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { + // Functions with any parameters that lack type annotations are context sensitive. + if (some(node.parameters, p => !getEffectiveTypeAnnotationNode(p))) { + return true; + } + if (node.kind !== SyntaxKind.ArrowFunction) { + // If the first parameter is not an explicit 'this' parameter, then the function has + // an implicit 'this' parameter which is subject to contextual typing. + const parameter = firstOrUndefined(node.parameters); + if (!(parameter && parameterIsThisKeyword(parameter))) { return true; } - if (node.kind !== SyntaxKind.ArrowFunction) { - // If the first parameter is not an explicit 'this' parameter, then the function has - // an implicit 'this' parameter which is subject to contextual typing. - const parameter = firstOrUndefined(node.parameters); - if (!(parameter && parameterIsThisKeyword(parameter))) { - return true; - } - } } return false; } diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt b/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt deleted file mode 100644 index fcacb3c60dfc5..0000000000000 --- a/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt +++ /dev/null @@ -1,62 +0,0 @@ -contextualTypingGenericFunction2.ts(40,22): error TS18046: 'a' is of type 'unknown'. - - -==== contextualTypingGenericFunction2.ts (1 errors) ==== - // https://github.com/microsoft/TypeScript/issues/61791 - - declare const fn1: , Ret>( - self: T, - body: (this: T, ...args: Args) => Ret, - ) => (...args: Args) => Ret; - - export const ok1 = fn1({ message: "foo" }, function (n: number) { - this.message; - }); - - export const ok2 = fn1({ message: "foo" }, function (n: N) { - this.message; - }); - - declare const fn2: , Ret>( - body: (first: string, ...args: Args) => Ret, - ) => (...args: Args) => Ret; - - export const ok3 = fn2(function (first, n: N) {}); - - declare const fn3: , Ret>( - body: (...args: Args) => (arg: string) => Ret, - ) => (...args: Args) => Ret; - - export const ok4 = fn3(function (n: N) { - return (arg) => { - return 10 - } - }); - - declare function fn4(config: { - context: T; - callback: (params: P) => (context: T, params: P) => number; - }): (params: P) => number; - - export const ok5 = fn4({ - context: 1, - callback: (params: T) => { - return (a, b) => a + 1; - ~ -!!! error TS18046: 'a' is of type 'unknown'. - }, - }); - - declare const fnGen1: , Ret>( - self: T, - body: (this: T, ...args: Args) => Generator, - ) => (...args: Args) => Ret; - - export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { - this.message; - }); - - export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { - this.message; - }); - \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.js b/tests/baselines/reference/contextualTypingGenericFunction2.js index 712b4155fae22..5f06aa6fc7b8b 100644 --- a/tests/baselines/reference/contextualTypingGenericFunction2.js +++ b/tests/baselines/reference/contextualTypingGenericFunction2.js @@ -62,9 +62,28 @@ export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { //// [contextualTypingGenericFunction2.d.ts] export declare const ok1: (n: number) => void; -export declare const ok2: (n: N) => void; +export declare const ok2: (n: any) => void; export declare const ok3: (n: N) => void; export declare const ok4: (n: N) => number; -export declare const ok5: (params: T) => number; +export declare const ok5: (params: T) => number; export declare const ok6: (n: number) => void; -export declare const ok7: (n: N) => void; +export declare const ok7: (n: any) => void; + + +//// [DtsFileErrors] + + +contextualTypingGenericFunction2.d.ts(5,36): error TS2304: Cannot find name 'T'. + + +==== contextualTypingGenericFunction2.d.ts (1 errors) ==== + export declare const ok1: (n: number) => void; + export declare const ok2: (n: any) => void; + export declare const ok3: (n: N) => void; + export declare const ok4: (n: N) => number; + export declare const ok5: (params: T) => number; + ~ +!!! error TS2304: Cannot find name 'T'. + export declare const ok6: (n: number) => void; + export declare const ok7: (n: any) => void; + \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.types b/tests/baselines/reference/contextualTypingGenericFunction2.types index 6ec974c024a79..970aef92e6806 100644 --- a/tests/baselines/reference/contextualTypingGenericFunction2.types +++ b/tests/baselines/reference/contextualTypingGenericFunction2.types @@ -56,10 +56,10 @@ export const ok1 = fn1({ message: "foo" }, function (n: number) { }); export const ok2 = fn1({ message: "foo" }, function (n: N) { ->ok2 : (n: N) => void -> : ^ ^^^^^^^^^^^^^^^ ->fn1({ message: "foo" }, function (n: N) { this.message;}) : (n: N) => void -> : ^ ^^^^^^^^^^^^^^^ +>ok2 : (n: any) => void +> : ^^^^^^^^^^^^^^^^ +>fn1({ message: "foo" }, function (n: N) { this.message;}) : (n: any) => void +> : ^^^^^^^^^^^^^^^^ >fn1 : , Ret>(self: T, body: (this: T, ...args: Args) => Ret) => (...args: Args) => Ret > : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ >{ message: "foo" } : { message: string; } @@ -178,14 +178,14 @@ declare function fn4(config: { > : ^ export const ok5 = fn4({ ->ok5 : (params: T) => number -> : ^ ^^ ^^^^^^^^ ->fn4({ context: 1, callback: (params: T) => { return (a, b) => a + 1; },}) : (params: T) => number -> : ^ ^^ ^^^^^^^^ +>ok5 : (params: T) => number +> : ^ ^^^^^^^^ +>fn4({ context: 1, callback: (params: T) => { return (a, b) => a + 1; },}) : (params: T) => number +> : ^ ^^^^^^^^ >fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; }) => (params: P) => number > : ^ ^^ ^^ ^^ ^^^^^ ->{ context: 1, callback: (params: T) => { return (a, b) => a + 1; },} : { context: number; callback: (params: T) => (a: unknown, b: unknown) => any; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ +>{ context: 1, callback: (params: T) => { return (a, b) => a + 1; },} : { context: number; callback: (params: T) => (a: number, b: T) => number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ context: 1, >context : number @@ -194,24 +194,24 @@ export const ok5 = fn4({ > : ^ callback: (params: T) => { ->callback : (params: T) => (a: unknown, b: unknown) => any -> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ->(params: T) => { return (a, b) => a + 1; } : (params: T) => (a: unknown, b: unknown) => any -> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +>callback : (params: T) => (a: number, b: T) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ +>(params: T) => { return (a, b) => a + 1; } : (params: T) => (a: number, b: T) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ >params : T > : ^ return (a, b) => a + 1; ->(a, b) => a + 1 : (a: unknown, b: unknown) => any -> : ^ ^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ ->a : unknown -> : ^^^^^^^ ->b : unknown -> : ^^^^^^^ ->a + 1 : any -> : ^^^ ->a : unknown -> : ^^^^^^^ +>(a, b) => a + 1 : (a: number, b: T) => number +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>b : T +> : ^ +>a + 1 : number +> : ^^^^^^ +>a : number +> : ^^^^^^ >1 : 1 > : ^ @@ -267,10 +267,10 @@ export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { }); export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { ->ok7 : (n: N) => void -> : ^ ^^^^^^^^^^^^^^^ ->fnGen1({ message: "foo" }, function* (n: N) { this.message;}) : (n: N) => void -> : ^ ^^^^^^^^^^^^^^^ +>ok7 : (n: any) => void +> : ^^^^^^^^^^^^^^^^ +>fnGen1({ message: "foo" }, function* (n: N) { this.message;}) : (n: any) => void +> : ^^^^^^^^^^^^^^^^ >fnGen1 : , Ret>(self: T, body: (this: T, ...args: Args) => Generator) => (...args: Args) => Ret > : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ >{ message: "foo" } : { message: string; } diff --git a/tests/baselines/reference/literalTypes2.types b/tests/baselines/reference/literalTypes2.types index 8190a61a91afd..2744be4c5f10b 100644 --- a/tests/baselines/reference/literalTypes2.types +++ b/tests/baselines/reference/literalTypes2.types @@ -375,9 +375,9 @@ function f3() { > : ^^^^^ const c3 = cond ? E.A : cond ? true : 123; ->c3 : true | 123 | E.A +>c3 : true | E.A | 123 > : ^^^^^^^^^^^^^^^^ ->cond ? E.A : cond ? true : 123 : true | 123 | E.A +>cond ? E.A : cond ? true : 123 : true | E.A | 123 > : ^^^^^^^^^^^^^^^^ >cond : boolean > : ^^^^^^^ @@ -473,7 +473,7 @@ function f3() { let x3 = c3; >x3 : number | boolean > : ^^^^^^^^^^^^^^^^ ->c3 : true | 123 | E.A +>c3 : true | E.A | 123 > : ^^^^^^^^^^^^^^^^ let x4 = c4; diff --git a/tests/cases/fourslash/contextualTypingGenericFunction1.ts b/tests/cases/fourslash/contextualTypingGenericFunction1.ts index 9fa6b92da237a..31411f72609c9 100644 --- a/tests/cases/fourslash/contextualTypingGenericFunction1.ts +++ b/tests/cases/fourslash/contextualTypingGenericFunction1.ts @@ -1,6 +1,5 @@ /// -// should not contextually type the RHS because it introduces type parameters ////var obj: { f(x: T): T } = { f: (/*1*/x) => x }; ////var obj2: (x: T) => T = (/*2*/x) => x; //// @@ -11,7 +10,7 @@ ////c.obj = (/*3*/x) => x; verify.quickInfos({ - 1: "(parameter) x: any", - 2: "(parameter) x: any", - 3: "(parameter) x: any" + 1: "(parameter) x: T", + 2: "(parameter) x: T", + 3: "(parameter) x: T" }); From 3db0dc6bd1c899372c2c6e4c918bdabad6f157b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 30 Jun 2025 10:29:01 +0200 Subject: [PATCH 4/4] Just assign those contextual parameter types for now, without any attempt to hoist inferred type parameters --- src/compiler/checker.ts | 17 +- src/compiler/types.ts | 1 + ...ontextualTypingGenericFunction2.errors.txt | 98 ++++++++ .../contextualTypingGenericFunction2.js | 79 +++--- .../contextualTypingGenericFunction2.symbols | 220 +++++++++++------ .../contextualTypingGenericFunction2.types | 233 +++++++++++++++--- .../contextualTypingGenericFunction2.ts | 43 +++- 7 files changed, 537 insertions(+), 154 deletions(-) create mode 100644 tests/baselines/reference/contextualTypingGenericFunction2.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 571a75daa16bc..3a52516c3491d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -38452,6 +38452,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function inferFromAnnotatedParametersAndReturn(signature: Signature, context: Signature, inferenceContext: InferenceContext) { + if (inferenceContext.flags & InferenceFlags.IgnoreAnnotatedParametersAndReturns) { + return; + } const len = signature.parameters.length - (signatureHasRestParameter(signature) ? 1 : 0); for (let i = 0; i < len; i++) { const declaration = signature.parameters[i].valueDeclaration as ParameterDeclaration; @@ -39460,9 +39463,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!signature) { return; } + let saveInferenceFlags: InferenceFlags = InferenceFlags.None; + const inferenceContext = getInferenceContext(node); + if (inferenceContext) { + saveInferenceFlags = inferenceContext.flags; + // inferring from those could possibly infer inner type parameters into outer inference context + // and they could potentially leak in the final inferred type as `instantiateTypeWithSingleGenericCallSignature` + // can only "hoist" them using `context.inferredTypeParameters` in a narrow set of cases + inferenceContext.flags |= node.typeParameters ? InferenceFlags.IgnoreAnnotatedParametersAndReturns : InferenceFlags.None; + } if (isContextSensitive(node)) { if (contextualSignature) { - const inferenceContext = getInferenceContext(node); let instantiatedContextualSignature: Signature | undefined; if (checkMode && checkMode & CheckMode.Inferential) { inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!); @@ -39481,7 +39492,6 @@ 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) { inferFromAnnotatedParametersAndReturn(signature, contextualSignature, inferenceContext!); } @@ -39492,6 +39502,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { signature.resolvedReturnType = returnType; } } + if (inferenceContext) { + inferenceContext.flags = saveInferenceFlags; + } checkSignatureDeclaration(node); } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ee7f4338a3a10..9669a86b77ce9 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7129,6 +7129,7 @@ export const enum InferenceFlags { NoDefault = 1 << 0, // Infer silentNeverType for no inferences (otherwise anyType or unknownType) AnyDefault = 1 << 1, // Infer anyType (in JS files) for no inferences (otherwise unknownType) SkippedGenericFunction = 1 << 2, // A generic function was skipped during inference + IgnoreAnnotatedParametersAndReturns = 1 << 3, // used when assigning contextual parameters types within generic functions } /** diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt b/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt new file mode 100644 index 0000000000000..36324c987475c --- /dev/null +++ b/tests/baselines/reference/contextualTypingGenericFunction2.errors.txt @@ -0,0 +1,98 @@ +contextualTypingGenericFunction2.ts(55,3): error TS2322: Type 'number' is not assignable to type 'boolean'. +contextualTypingGenericFunction2.ts(65,3): error TS2322: Type '(params: N) => (a: number, b: unknown) => boolean' is not assignable to type '(params: unknown) => (context: number, params: unknown) => number'. + Call signature return types '(a: number, b: unknown) => boolean' and '(context: number, params: unknown) => number' are incompatible. + Type 'boolean' is not assignable to type 'number'. + + +==== contextualTypingGenericFunction2.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/61791 + + declare const fn1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Ret, + ) => (...args: Args) => Ret; + + export const result1 = fn1({ message: "foo" }, function (n: number) { + this.message; + }); + + export const result2 = fn1({ message: "foo" }, function (n: N) { + this.message; + }); + + declare const fn2: , Ret>( + body: (first: string, ...args: Args) => Ret, + ) => (...args: Args) => Ret; + + export const result3 = fn2(function (first, n: N) {}); + + declare const fn3: , Ret>( + body: (...args: Args) => (arg: string) => Ret, + ) => (...args: Args) => Ret; + + export const result4 = fn3(function (n: N) { + return (arg) => { + return 10 + } + }); + + declare function fn4(config: { + context: T; + callback: (params: P) => (context: T, params: P) => number; + other?: (arg: string) => void; + }): (params: P) => number; + + export const result5 = fn4({ + context: 1, + callback: (params: N) => { + return (a, b) => a + 1; + }, + }); + + export const result6 = fn4({ + context: 1, + callback: (params: N) => { + return (a, b) => a + 1; + }, + other: (_) => {} // outer context-sensitive function + }); + + // should error + export const result7 = fn4({ + context: 1, + ~~~~~~~ +!!! error TS2322: Type 'number' is not assignable to type 'boolean'. +!!! 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; }' + callback: (params: N) => { + return (a: boolean, b) => a ? 1 : 2; + }, + other: (_) => {} // outer context-sensitive function + }); + + // should error + export const result8 = fn4({ + context: 1, + callback: (params: N) => { + ~~~~~~~~ +!!! error TS2322: Type '(params: N) => (a: number, b: unknown) => boolean' is not assignable to type '(params: unknown) => (context: number, params: unknown) => number'. +!!! error TS2322: Call signature return types '(a: number, b: unknown) => boolean' and '(context: number, params: unknown) => number' are incompatible. +!!! error TS2322: Type 'boolean' is not assignable to type 'number'. +!!! 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; }' + return (a, b) => true; + }, + other: (_) => {} // outer context-sensitive function + }); + + declare const fnGen1: , Ret>( + self: T, + body: (this: T, ...args: Args) => Generator, + ) => (...args: Args) => Ret; + + export const result9 = fnGen1({ message: "foo" }, function* (n: number) { + this.message; + }); + + export const result10 = fnGen1({ message: "foo" }, function* (n: N) { + this.message; + }); + \ No newline at end of file diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.js b/tests/baselines/reference/contextualTypingGenericFunction2.js index 5f06aa6fc7b8b..37c91b01118cc 100644 --- a/tests/baselines/reference/contextualTypingGenericFunction2.js +++ b/tests/baselines/reference/contextualTypingGenericFunction2.js @@ -8,11 +8,11 @@ declare const fn1: , Ret>( body: (this: T, ...args: Args) => Ret, ) => (...args: Args) => Ret; -export const ok1 = fn1({ message: "foo" }, function (n: number) { +export const result1 = fn1({ message: "foo" }, function (n: number) { this.message; }); -export const ok2 = fn1({ message: "foo" }, function (n: N) { +export const result2 = fn1({ message: "foo" }, function (n: N) { this.message; }); @@ -20,13 +20,13 @@ declare const fn2: , Ret>( body: (first: string, ...args: Args) => Ret, ) => (...args: Args) => Ret; -export const ok3 = fn2(function (first, n: N) {}); +export const result3 = fn2(function (first, n: N) {}); declare const fn3: , Ret>( body: (...args: Args) => (arg: string) => Ret, ) => (...args: Args) => Ret; -export const ok4 = fn3(function (n: N) { +export const result4 = fn3(function (n: N) { return (arg) => { return 10 } @@ -35,25 +35,52 @@ export const ok4 = fn3(function (n: N) { declare function fn4(config: { context: T; callback: (params: P) => (context: T, params: P) => number; + other?: (arg: string) => void; }): (params: P) => number; -export const ok5 = fn4({ +export const result5 = fn4({ context: 1, - callback: (params: T) => { + callback: (params: N) => { return (a, b) => a + 1; }, }); +export const result6 = fn4({ + context: 1, + callback: (params: N) => { + return (a, b) => a + 1; + }, + other: (_) => {} // outer context-sensitive function +}); + +// should error +export const result7 = fn4({ + context: 1, + callback: (params: N) => { + return (a: boolean, b) => a ? 1 : 2; + }, + other: (_) => {} // outer context-sensitive function +}); + + // should error +export const result8 = fn4({ + context: 1, + callback: (params: N) => { + return (a, b) => true; + }, + other: (_) => {} // outer context-sensitive function +}); + declare const fnGen1: , Ret>( self: T, body: (this: T, ...args: Args) => Generator, ) => (...args: Args) => Ret; -export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { +export const result9 = fnGen1({ message: "foo" }, function* (n: number) { this.message; }); -export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { +export const result10 = fnGen1({ message: "foo" }, function* (n: N) { this.message; }); @@ -61,29 +88,13 @@ export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { //// [contextualTypingGenericFunction2.d.ts] -export declare const ok1: (n: number) => void; -export declare const ok2: (n: any) => void; -export declare const ok3: (n: N) => void; -export declare const ok4: (n: N) => number; -export declare const ok5: (params: T) => number; -export declare const ok6: (n: number) => void; -export declare const ok7: (n: any) => void; - - -//// [DtsFileErrors] - - -contextualTypingGenericFunction2.d.ts(5,36): error TS2304: Cannot find name 'T'. - - -==== contextualTypingGenericFunction2.d.ts (1 errors) ==== - export declare const ok1: (n: number) => void; - export declare const ok2: (n: any) => void; - export declare const ok3: (n: N) => void; - export declare const ok4: (n: N) => number; - export declare const ok5: (params: T) => number; - ~ -!!! error TS2304: Cannot find name 'T'. - export declare const ok6: (n: number) => void; - export declare const ok7: (n: any) => void; - \ No newline at end of file +export declare const result1: (n: number) => void; +export declare const result2: (n: any) => void; +export declare const result3: (n: N) => void; +export declare const result4: (n: N) => number; +export declare const result5: (params: unknown) => number; +export declare const result6: (params: unknown) => number; +export declare const result7: (params: unknown) => number; +export declare const result8: (params: unknown) => number; +export declare const result9: (n: number) => void; +export declare const result10: (n: any) => void; diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.symbols b/tests/baselines/reference/contextualTypingGenericFunction2.symbols index 16c60357bd75f..278d61d327d08 100644 --- a/tests/baselines/reference/contextualTypingGenericFunction2.symbols +++ b/tests/baselines/reference/contextualTypingGenericFunction2.symbols @@ -27,31 +27,31 @@ declare const fn1: , Ret>( >Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 2, 22)) >Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 2, 47)) -export const ok1 = fn1({ message: "foo" }, function (n: number) { ->ok1 : Symbol(ok1, Decl(contextualTypingGenericFunction2.ts, 7, 12)) +export const result1 = fn1({ message: "foo" }, function (n: number) { +>result1 : Symbol(result1, Decl(contextualTypingGenericFunction2.ts, 7, 12)) >fn1 : Symbol(fn1, Decl(contextualTypingGenericFunction2.ts, 2, 13)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 24)) ->n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 7, 53)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 28)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 7, 57)) this.message; ->this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 24)) +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 28)) >this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 4, 9)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 24)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 7, 28)) }); -export const ok2 = fn1({ message: "foo" }, function (n: N) { ->ok2 : Symbol(ok2, Decl(contextualTypingGenericFunction2.ts, 11, 12)) +export const result2 = fn1({ message: "foo" }, function (n: N) { +>result2 : Symbol(result2, Decl(contextualTypingGenericFunction2.ts, 11, 12)) >fn1 : Symbol(fn1, Decl(contextualTypingGenericFunction2.ts, 2, 13)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 24)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 11, 53)) ->n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 11, 56)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 11, 53)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 28)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 11, 57)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 11, 60)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 11, 57)) this.message; ->this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 24)) +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 28)) >this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 4, 9)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 24)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 11, 28)) }); @@ -73,13 +73,13 @@ declare const fn2: , Ret>( >Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 15, 20)) >Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 15, 44)) -export const ok3 = fn2(function (first, n: N) {}); ->ok3 : Symbol(ok3, Decl(contextualTypingGenericFunction2.ts, 19, 12)) +export const result3 = fn2(function (first, n: N) {}); +>result3 : Symbol(result3, Decl(contextualTypingGenericFunction2.ts, 19, 12)) >fn2 : Symbol(fn2, Decl(contextualTypingGenericFunction2.ts, 15, 13)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 19, 33)) ->first : Symbol(first, Decl(contextualTypingGenericFunction2.ts, 19, 36)) ->n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 19, 42)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 19, 33)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 19, 37)) +>first : Symbol(first, Decl(contextualTypingGenericFunction2.ts, 19, 40)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 19, 46)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 19, 37)) declare const fn3: , Ret>( >fn3 : Symbol(fn3, Decl(contextualTypingGenericFunction2.ts, 21, 13)) @@ -99,12 +99,12 @@ declare const fn3: , Ret>( >Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 21, 20)) >Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 21, 44)) -export const ok4 = fn3(function (n: N) { ->ok4 : Symbol(ok4, Decl(contextualTypingGenericFunction2.ts, 25, 12)) +export const result4 = fn3(function (n: N) { +>result4 : Symbol(result4, Decl(contextualTypingGenericFunction2.ts, 25, 12)) >fn3 : Symbol(fn3, Decl(contextualTypingGenericFunction2.ts, 21, 13)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 25, 33)) ->n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 25, 36)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 25, 33)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 25, 37)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 25, 40)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 25, 37)) return (arg) => { >arg : Symbol(arg, Decl(contextualTypingGenericFunction2.ts, 26, 12)) @@ -132,81 +132,161 @@ declare function fn4(config: { >params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 33, 39)) >P : Symbol(P, Decl(contextualTypingGenericFunction2.ts, 31, 23)) + other?: (arg: string) => void; +>other : Symbol(other, Decl(contextualTypingGenericFunction2.ts, 33, 61)) +>arg : Symbol(arg, Decl(contextualTypingGenericFunction2.ts, 34, 11)) + }): (params: P) => number; ->params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 34, 5)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 35, 5)) >P : Symbol(P, Decl(contextualTypingGenericFunction2.ts, 31, 23)) -export const ok5 = fn4({ ->ok5 : Symbol(ok5, Decl(contextualTypingGenericFunction2.ts, 36, 12)) +export const result5 = fn4({ +>result5 : Symbol(result5, Decl(contextualTypingGenericFunction2.ts, 37, 12)) +>fn4 : Symbol(fn4, Decl(contextualTypingGenericFunction2.ts, 29, 3)) + + context: 1, +>context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 37, 28)) + + callback: (params: N) => { +>callback : Symbol(callback, Decl(contextualTypingGenericFunction2.ts, 38, 13)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 39, 13)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 39, 17)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 39, 13)) + + return (a, b) => a + 1; +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 40, 12)) +>b : Symbol(b, Decl(contextualTypingGenericFunction2.ts, 40, 14)) +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 40, 12)) + + }, +}); + +export const result6 = fn4({ +>result6 : Symbol(result6, Decl(contextualTypingGenericFunction2.ts, 44, 12)) >fn4 : Symbol(fn4, Decl(contextualTypingGenericFunction2.ts, 29, 3)) context: 1, ->context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 36, 24)) +>context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 44, 28)) - callback: (params: T) => { ->callback : Symbol(callback, Decl(contextualTypingGenericFunction2.ts, 37, 13)) ->T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 38, 13)) ->params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 38, 17)) ->T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 38, 13)) + callback: (params: N) => { +>callback : Symbol(callback, Decl(contextualTypingGenericFunction2.ts, 45, 13)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 46, 13)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 46, 17)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 46, 13)) return (a, b) => a + 1; ->a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 39, 12)) ->b : Symbol(b, Decl(contextualTypingGenericFunction2.ts, 39, 14)) ->a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 39, 12)) +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 47, 12)) +>b : Symbol(b, Decl(contextualTypingGenericFunction2.ts, 47, 14)) +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 47, 12)) + + }, + other: (_) => {} // outer context-sensitive function +>other : Symbol(other, Decl(contextualTypingGenericFunction2.ts, 48, 4)) +>_ : Symbol(_, Decl(contextualTypingGenericFunction2.ts, 49, 10)) + +}); + +// should error +export const result7 = fn4({ +>result7 : Symbol(result7, Decl(contextualTypingGenericFunction2.ts, 53, 12)) +>fn4 : Symbol(fn4, Decl(contextualTypingGenericFunction2.ts, 29, 3)) + + context: 1, +>context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 53, 28)) + + callback: (params: N) => { +>callback : Symbol(callback, Decl(contextualTypingGenericFunction2.ts, 54, 13)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 55, 13)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 55, 17)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 55, 13)) + + return (a: boolean, b) => a ? 1 : 2; +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 56, 12)) +>b : Symbol(b, Decl(contextualTypingGenericFunction2.ts, 56, 23)) +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 56, 12)) + + }, + other: (_) => {} // outer context-sensitive function +>other : Symbol(other, Decl(contextualTypingGenericFunction2.ts, 57, 4)) +>_ : Symbol(_, Decl(contextualTypingGenericFunction2.ts, 58, 10)) + +}); + + // should error +export const result8 = fn4({ +>result8 : Symbol(result8, Decl(contextualTypingGenericFunction2.ts, 62, 12)) +>fn4 : Symbol(fn4, Decl(contextualTypingGenericFunction2.ts, 29, 3)) + + context: 1, +>context : Symbol(context, Decl(contextualTypingGenericFunction2.ts, 62, 28)) + + callback: (params: N) => { +>callback : Symbol(callback, Decl(contextualTypingGenericFunction2.ts, 63, 13)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 64, 13)) +>params : Symbol(params, Decl(contextualTypingGenericFunction2.ts, 64, 17)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 64, 13)) + + return (a, b) => true; +>a : Symbol(a, Decl(contextualTypingGenericFunction2.ts, 65, 12)) +>b : Symbol(b, Decl(contextualTypingGenericFunction2.ts, 65, 14)) }, + other: (_) => {} // outer context-sensitive function +>other : Symbol(other, Decl(contextualTypingGenericFunction2.ts, 66, 4)) +>_ : Symbol(_, Decl(contextualTypingGenericFunction2.ts, 67, 10)) + }); declare const fnGen1: , Ret>( ->fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 43, 13)) ->T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 43, 23)) ->Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 43, 25)) +>fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 70, 13)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 70, 23)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 70, 25)) >Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) ->Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 43, 50)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 70, 50)) self: T, ->self : Symbol(self, Decl(contextualTypingGenericFunction2.ts, 43, 56)) ->T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 43, 23)) +>self : Symbol(self, Decl(contextualTypingGenericFunction2.ts, 70, 56)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 70, 23)) body: (this: T, ...args: Args) => Generator, ->body : Symbol(body, Decl(contextualTypingGenericFunction2.ts, 44, 10)) ->this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 45, 9)) ->T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 43, 23)) ->args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 45, 17)) ->Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 43, 25)) +>body : Symbol(body, Decl(contextualTypingGenericFunction2.ts, 71, 10)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 72, 9)) +>T : Symbol(T, Decl(contextualTypingGenericFunction2.ts, 70, 23)) +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 72, 17)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 70, 25)) >Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) ->Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 43, 50)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 70, 50)) ) => (...args: Args) => Ret; ->args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 46, 6)) ->Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 43, 25)) ->Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 43, 50)) +>args : Symbol(args, Decl(contextualTypingGenericFunction2.ts, 73, 6)) +>Args : Symbol(Args, Decl(contextualTypingGenericFunction2.ts, 70, 25)) +>Ret : Symbol(Ret, Decl(contextualTypingGenericFunction2.ts, 70, 50)) -export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { ->ok6 : Symbol(ok6, Decl(contextualTypingGenericFunction2.ts, 48, 12)) ->fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 43, 13)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 48, 27)) ->n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 48, 57)) +export const result9 = fnGen1({ message: "foo" }, function* (n: number) { +>result9 : Symbol(result9, Decl(contextualTypingGenericFunction2.ts, 75, 12)) +>fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 70, 13)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 75, 31)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 75, 61)) this.message; ->this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 48, 27)) ->this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 45, 9)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 48, 27)) +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 75, 31)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 72, 9)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 75, 31)) }); -export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { ->ok7 : Symbol(ok7, Decl(contextualTypingGenericFunction2.ts, 52, 12)) ->fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 43, 13)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 52, 27)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 52, 57)) ->n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 52, 60)) ->N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 52, 57)) +export const result10 = fnGen1({ message: "foo" }, function* (n: N) { +>result10 : Symbol(result10, Decl(contextualTypingGenericFunction2.ts, 79, 12)) +>fnGen1 : Symbol(fnGen1, Decl(contextualTypingGenericFunction2.ts, 70, 13)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 79, 32)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 79, 62)) +>n : Symbol(n, Decl(contextualTypingGenericFunction2.ts, 79, 65)) +>N : Symbol(N, Decl(contextualTypingGenericFunction2.ts, 79, 62)) this.message; ->this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 52, 27)) ->this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 45, 9)) ->message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 52, 27)) +>this.message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 79, 32)) +>this : Symbol(this, Decl(contextualTypingGenericFunction2.ts, 72, 9)) +>message : Symbol(message, Decl(contextualTypingGenericFunction2.ts, 79, 32)) }); diff --git a/tests/baselines/reference/contextualTypingGenericFunction2.types b/tests/baselines/reference/contextualTypingGenericFunction2.types index 970aef92e6806..2d2d10f40bd49 100644 --- a/tests/baselines/reference/contextualTypingGenericFunction2.types +++ b/tests/baselines/reference/contextualTypingGenericFunction2.types @@ -27,9 +27,9 @@ declare const fn1: , Ret>( >args : Args > : ^^^^ -export const ok1 = fn1({ message: "foo" }, function (n: number) { ->ok1 : (n: number) => void -> : ^^^^^^^^^^^^^^^^^^^ +export const result1 = fn1({ message: "foo" }, function (n: number) { +>result1 : (n: number) => void +> : ^^^^^^^^^^^^^^^^^^^ >fn1({ message: "foo" }, function (n: number) { this.message;}) : (n: number) => void > : ^^^^^^^^^^^^^^^^^^^ >fn1 : , Ret>(self: T, body: (this: T, ...args: Args) => Ret) => (...args: Args) => Ret @@ -55,9 +55,9 @@ export const ok1 = fn1({ message: "foo" }, function (n: number) { }); -export const ok2 = fn1({ message: "foo" }, function (n: N) { ->ok2 : (n: any) => void -> : ^^^^^^^^^^^^^^^^ +export const result2 = fn1({ message: "foo" }, function (n: N) { +>result2 : (n: any) => void +> : ^^^^^^^^^^^^^^^^ >fn1({ message: "foo" }, function (n: N) { this.message;}) : (n: any) => void > : ^^^^^^^^^^^^^^^^ >fn1 : , Ret>(self: T, body: (this: T, ...args: Args) => Ret) => (...args: Args) => Ret @@ -99,9 +99,9 @@ declare const fn2: , Ret>( >args : Args > : ^^^^ -export const ok3 = fn2(function (first, n: N) {}); ->ok3 : (n: N) => void -> : ^ ^^^^^^^^^^^^^^^ +export const result3 = fn2(function (first, n: N) {}); +>result3 : (n: N) => void +> : ^ ^^^^^^^^^^^^^^^ >fn2(function (first, n: N) {}) : (n: N) => void > : ^ ^^^^^^^^^^^^^^^ >fn2 : , Ret>(body: (first: string, ...args: Args) => Ret) => (...args: Args) => Ret @@ -129,9 +129,9 @@ declare const fn3: , Ret>( >args : Args > : ^^^^ -export const ok4 = fn3(function (n: N) { ->ok4 : (n: N) => number -> : ^ ^^^^^^^^^^^^^^^^^ +export const result4 = fn3(function (n: N) { +>result4 : (n: N) => number +> : ^ ^^^^^^^^^^^^^^^^^ >fn3(function (n: N) { return (arg) => { return 10 }}) : (n: N) => number > : ^ ^^^^^^^^^^^^^^^^^ >fn3 : , Ret>(body: (...args: Args) => (arg: string) => Ret) => (...args: Args) => Ret @@ -154,10 +154,10 @@ export const ok4 = fn3(function (n: N) { }); declare function fn4(config: { ->fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; }) => (params: P) => number -> : ^ ^^ ^^ ^^ ^^^^^ ->config : { context: T; callback: (params: P) => (context: T, params: P) => number; } -> : ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^ +>fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; other?: (arg: string) => void; }) => (params: P) => number +> : ^ ^^ ^^ ^^ ^^^^^ +>config : { context: T; callback: (params: P) => (context: T, params: P) => number; other?: (arg: string) => void; } +> : ^^^^^^^^^^^ ^^^^^^^^^^^^ ^^^^^^^^^^ ^^^ context: T; >context : T @@ -173,19 +173,25 @@ declare function fn4(config: { >params : P > : ^ + other?: (arg: string) => void; +>other : ((arg: string) => void) | undefined +> : ^^ ^^ ^^^^^ ^^^^^^^^^^^^^ +>arg : string +> : ^^^^^^ + }): (params: P) => number; >params : P > : ^ -export const ok5 = fn4({ ->ok5 : (params: T) => number -> : ^ ^^^^^^^^ ->fn4({ context: 1, callback: (params: T) => { return (a, b) => a + 1; },}) : (params: T) => number -> : ^ ^^^^^^^^ ->fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; }) => (params: P) => number -> : ^ ^^ ^^ ^^ ^^^^^ ->{ context: 1, callback: (params: T) => { return (a, b) => a + 1; },} : { context: number; callback: (params: T) => (a: number, b: T) => number; } -> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ +export const result5 = fn4({ +>result5 : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4({ context: 1, callback: (params: N) => { return (a, b) => a + 1; },}) : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; other?: (arg: string) => void; }) => (params: P) => number +> : ^ ^^ ^^ ^^ ^^^^^ +>{ context: 1, callback: (params: N) => { return (a, b) => a + 1; },} : { context: number; callback: (params: N) => (a: number, b: unknown) => number; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^ context: 1, >context : number @@ -193,21 +199,62 @@ export const ok5 = fn4({ >1 : 1 > : ^ - callback: (params: T) => { ->callback : (params: T) => (a: number, b: T) => number -> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ ->(params: T) => { return (a, b) => a + 1; } : (params: T) => (a: number, b: T) => number -> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ ->params : T + callback: (params: N) => { +>callback : (params: N) => (a: number, b: unknown) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ +>(params: N) => { return (a, b) => a + 1; } : (params: N) => (a: number, b: unknown) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ +>params : N > : ^ return (a, b) => a + 1; ->(a, b) => a + 1 : (a: number, b: T) => number -> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^ +>(a, b) => a + 1 : (a: number, b: unknown) => number +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>b : unknown +> : ^^^^^^^ +>a + 1 : number +> : ^^^^^^ >a : number > : ^^^^^^ ->b : T +>1 : 1 > : ^ + + }, +}); + +export const result6 = fn4({ +>result6 : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4({ context: 1, callback: (params: N) => { return (a, b) => a + 1; }, other: (_) => {} // outer context-sensitive function}) : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; other?: (arg: string) => void; }) => (params: P) => number +> : ^ ^^ ^^ ^^ ^^^^^ +>{ context: 1, callback: (params: N) => { return (a, b) => a + 1; }, other: (_) => {} // outer context-sensitive function} : { context: number; callback: (params: N) => (a: number, b: unknown) => number; other: (_: string) => void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + + context: 1, +>context : number +> : ^^^^^^ +>1 : 1 +> : ^ + + callback: (params: N) => { +>callback : (params: N) => (a: number, b: unknown) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ +>(params: N) => { return (a, b) => a + 1; } : (params: N) => (a: number, b: unknown) => number +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ +>params : N +> : ^ + + return (a, b) => a + 1; +>(a, b) => a + 1 : (a: number, b: unknown) => number +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>b : unknown +> : ^^^^^^^ >a + 1 : number > : ^^^^^^ >a : number @@ -216,6 +263,112 @@ export const ok5 = fn4({ > : ^ }, + other: (_) => {} // outer context-sensitive function +>other : (_: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>(_) => {} : (_: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>_ : string +> : ^^^^^^ + +}); + +// should error +export const result7 = fn4({ +>result7 : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4({ context: 1, callback: (params: N) => { return (a: boolean, b) => a ? 1 : 2; }, other: (_) => {} // outer context-sensitive function}) : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; other?: (arg: string) => void; }) => (params: P) => number +> : ^ ^^ ^^ ^^ ^^^^^ +>{ context: 1, callback: (params: N) => { return (a: boolean, b) => a ? 1 : 2; }, other: (_) => {} // outer context-sensitive function} : { context: number; callback: (params: N) => (a: boolean, b: unknown) => 1 | 2; other: (_: string) => void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + + context: 1, +>context : number +> : ^^^^^^ +>1 : 1 +> : ^ + + callback: (params: N) => { +>callback : (params: N) => (a: boolean, b: unknown) => 1 | 2 +> : ^ ^^ ^^ ^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^ +>(params: N) => { return (a: boolean, b) => a ? 1 : 2; } : (params: N) => (a: boolean, b: unknown) => 1 | 2 +> : ^ ^^ ^^ ^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^ +>params : N +> : ^ + + return (a: boolean, b) => a ? 1 : 2; +>(a: boolean, b) => a ? 1 : 2 : (a: boolean, b: unknown) => 1 | 2 +> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^ +>a : boolean +> : ^^^^^^^ +>b : unknown +> : ^^^^^^^ +>a ? 1 : 2 : 1 | 2 +> : ^^^^^ +>a : boolean +> : ^^^^^^^ +>1 : 1 +> : ^ +>2 : 2 +> : ^ + + }, + other: (_) => {} // outer context-sensitive function +>other : (_: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>(_) => {} : (_: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>_ : string +> : ^^^^^^ + +}); + + // should error +export const result8 = fn4({ +>result8 : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4({ context: 1, callback: (params: N) => { return (a, b) => true; }, other: (_) => {} // outer context-sensitive function}) : (params: unknown) => number +> : ^ ^^^^^^^^^^^^^^ +>fn4 : (config: { context: T; callback: (params: P) => (context: T, params: P) => number; other?: (arg: string) => void; }) => (params: P) => number +> : ^ ^^ ^^ ^^ ^^^^^ +>{ context: 1, callback: (params: N) => { return (a, b) => true; }, other: (_) => {} // outer context-sensitive function} : { context: number; callback: (params: N) => (a: number, b: unknown) => boolean; other: (_: string) => void; } +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ + + context: 1, +>context : number +> : ^^^^^^ +>1 : 1 +> : ^ + + callback: (params: N) => { +>callback : (params: N) => (a: number, b: unknown) => boolean +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ +>(params: N) => { return (a, b) => true; } : (params: N) => (a: number, b: unknown) => boolean +> : ^ ^^ ^^ ^^^^^^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ +>params : N +> : ^ + + return (a, b) => true; +>(a, b) => true : (a: number, b: unknown) => boolean +> : ^ ^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^ +>a : number +> : ^^^^^^ +>b : unknown +> : ^^^^^^^ +>true : true +> : ^^^^ + + }, + other: (_) => {} // outer context-sensitive function +>other : (_: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>(_) => {} : (_: string) => void +> : ^ ^^^^^^^^^^^^^^^^^ +>_ : string +> : ^^^^^^ + }); declare const fnGen1: , Ret>( @@ -238,9 +391,9 @@ declare const fnGen1: , Ret>( >args : Args > : ^^^^ -export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { ->ok6 : (n: number) => void -> : ^^^^^^^^^^^^^^^^^^^ +export const result9 = fnGen1({ message: "foo" }, function* (n: number) { +>result9 : (n: number) => void +> : ^^^^^^^^^^^^^^^^^^^ >fnGen1({ message: "foo" }, function* (n: number) { this.message;}) : (n: number) => void > : ^^^^^^^^^^^^^^^^^^^ >fnGen1 : , Ret>(self: T, body: (this: T, ...args: Args) => Generator) => (...args: Args) => Ret @@ -266,9 +419,9 @@ export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { }); -export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { ->ok7 : (n: any) => void -> : ^^^^^^^^^^^^^^^^ +export const result10 = fnGen1({ message: "foo" }, function* (n: N) { +>result10 : (n: any) => void +> : ^^^^^^^^^^^^^^^^ >fnGen1({ message: "foo" }, function* (n: N) { this.message;}) : (n: any) => void > : ^^^^^^^^^^^^^^^^ >fnGen1 : , Ret>(self: T, body: (this: T, ...args: Args) => Generator) => (...args: Args) => Ret diff --git a/tests/cases/compiler/contextualTypingGenericFunction2.ts b/tests/cases/compiler/contextualTypingGenericFunction2.ts index 13a4e42c9baeb..0852c0cb0b1e8 100644 --- a/tests/cases/compiler/contextualTypingGenericFunction2.ts +++ b/tests/cases/compiler/contextualTypingGenericFunction2.ts @@ -10,11 +10,11 @@ declare const fn1: , Ret>( body: (this: T, ...args: Args) => Ret, ) => (...args: Args) => Ret; -export const ok1 = fn1({ message: "foo" }, function (n: number) { +export const result1 = fn1({ message: "foo" }, function (n: number) { this.message; }); -export const ok2 = fn1({ message: "foo" }, function (n: N) { +export const result2 = fn1({ message: "foo" }, function (n: N) { this.message; }); @@ -22,13 +22,13 @@ declare const fn2: , Ret>( body: (first: string, ...args: Args) => Ret, ) => (...args: Args) => Ret; -export const ok3 = fn2(function (first, n: N) {}); +export const result3 = fn2(function (first, n: N) {}); declare const fn3: , Ret>( body: (...args: Args) => (arg: string) => Ret, ) => (...args: Args) => Ret; -export const ok4 = fn3(function (n: N) { +export const result4 = fn3(function (n: N) { return (arg) => { return 10 } @@ -37,24 +37,51 @@ export const ok4 = fn3(function (n: N) { declare function fn4(config: { context: T; callback: (params: P) => (context: T, params: P) => number; + other?: (arg: string) => void; }): (params: P) => number; -export const ok5 = fn4({ +export const result5 = fn4({ context: 1, - callback: (params: T) => { + callback: (params: N) => { return (a, b) => a + 1; }, }); +export const result6 = fn4({ + context: 1, + callback: (params: N) => { + return (a, b) => a + 1; + }, + other: (_) => {} // outer context-sensitive function +}); + +// should error +export const result7 = fn4({ + context: 1, + callback: (params: N) => { + return (a: boolean, b) => a ? 1 : 2; + }, + other: (_) => {} // outer context-sensitive function +}); + + // should error +export const result8 = fn4({ + context: 1, + callback: (params: N) => { + return (a, b) => true; + }, + other: (_) => {} // outer context-sensitive function +}); + declare const fnGen1: , Ret>( self: T, body: (this: T, ...args: Args) => Generator, ) => (...args: Args) => Ret; -export const ok6 = fnGen1({ message: "foo" }, function* (n: number) { +export const result9 = fnGen1({ message: "foo" }, function* (n: number) { this.message; }); -export const ok7 = fnGen1({ message: "foo" }, function* (n: N) { +export const result10 = fnGen1({ message: "foo" }, function* (n: N) { this.message; });