diff --git a/src/transformation/utils/function-context.ts b/src/transformation/utils/function-context.ts index 8bf05255c..b3e9ee2c5 100644 --- a/src/transformation/utils/function-context.ts +++ b/src/transformation/utils/function-context.ts @@ -113,17 +113,25 @@ function getSignatureDeclarations( ): ts.SignatureDeclaration[] { return signatures.flatMap(signature => { const signatureDeclaration = signature.getDeclaration(); + let inferredType: ts.Type | undefined; if ( + ts.isMethodDeclaration(signatureDeclaration) && + ts.isObjectLiteralExpression(signatureDeclaration.parent) && + !getExplicitThisParameter(signatureDeclaration) + ) { + inferredType = context.checker.getContextualTypeForObjectLiteralElement(signatureDeclaration); + } else if ( (ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) && !getExplicitThisParameter(signatureDeclaration) ) { // Infer type of function expressions/arrow functions - const inferredType = inferAssignedType(context, signatureDeclaration); - if (inferredType) { - const inferredSignatures = getAllCallSignatures(inferredType); - if (inferredSignatures.length > 0) { - return inferredSignatures.map(s => s.getDeclaration()); - } + inferredType = inferAssignedType(context, signatureDeclaration); + } + + if (inferredType) { + const inferredSignatures = getAllCallSignatures(inferredType); + if (inferredSignatures.length > 0) { + return inferredSignatures.map(s => s.getDeclaration()); } } diff --git a/src/typescript-internal.d.ts b/src/typescript-internal.d.ts index 7bfd309ac..8fd8fd1ec 100644 --- a/src/typescript-internal.d.ts +++ b/src/typescript-internal.d.ts @@ -26,5 +26,6 @@ declare module "typescript" { interface TypeChecker { getElementTypeOfArrayType(type: Type): Type | undefined; + getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined; } } diff --git a/test/unit/objectLiteral.spec.ts b/test/unit/objectLiteral.spec.ts index 819143e1f..16e7582d0 100644 --- a/test/unit/objectLiteral.spec.ts +++ b/test/unit/objectLiteral.spec.ts @@ -63,3 +63,66 @@ test.each(['{x: "foobar"}.x', '{x: "foobar"}["x"]', '{x: () => "foobar"}.x()', ' util.testExpression(expression).expectToMatchJsResult(); } ); + +describe("noSelf in functions", () => { + test("Explicit this: void parameter", () => { + // language=TypeScript + util.testFunction` + const obj: Record string> = { + method(a) { + return a; + }, + func: function(a) { + return a; + }, + arrow: (a) => { + return a; + } + }; + return [obj.method("a") ?? "nil", obj.func("b") ?? "nil", obj.arrow("c") ?? "nil"]; + `.expectToMatchJsResult(); + }); + + test("No self annotation", () => { + // language=TypeScript + util.testFunction` + const obj: Record string> = { + method(a) { + return a; + }, + func: function(a) { + return a; + }, + arrow: (a) => { + return a; + } + }; + return [obj.method("a") ?? "nil", obj.func("b") ?? "nil", obj.arrow("c") ?? "nil"]; + `.expectToMatchJsResult(); + }); + + test("individual function types", () => { + // language=TypeScript + util.testFunction` + interface FunctionContainer { + method: (this: void, a: string) => string + func: (this: void, a: string) => string + arrow: (this: void, a: string) => string + } + + const obj: FunctionContainer = { + method(a) { + return a + }, + func: function(a) { + return a + }, + arrow: (a) => { + return a + } + } + + return [obj.method("a") ?? "nil", obj.func("b") ?? "nil", obj.arrow("c") ?? "nil"]; + `.expectToMatchJsResult(); + }); +});