From 9b39a61c80f5ab652b4e7eae4b06d2d3d3f2cc6b Mon Sep 17 00:00:00 2001 From: Benjamin Ye <24237065+enjoydambience@users.noreply.github.com> Date: Sun, 15 Aug 2021 11:44:36 -0700 Subject: [PATCH 1/3] Fix noSelf in object literal methods --- src/transformation/utils/function-context.ts | 18 ++++++----- src/typescript-internal.d.ts | 1 + test/unit/objectLiteral.spec.ts | 33 ++++++++++++++++++++ 3 files changed, 45 insertions(+), 7 deletions(-) diff --git a/src/transformation/utils/function-context.ts b/src/transformation/utils/function-context.ts index 8bf05255c..4d05458c3 100644 --- a/src/transformation/utils/function-context.ts +++ b/src/transformation/utils/function-context.ts @@ -113,17 +113,21 @@ function getSignatureDeclarations( ): ts.SignatureDeclaration[] { return signatures.flatMap(signature => { const signatureDeclaration = signature.getDeclaration(); - if ( + let inferredType: ts.Type | undefined; + if (ts.isMethodDeclaration(signatureDeclaration) && !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..88d590afe 100644 --- a/test/unit/objectLiteral.spec.ts +++ b/test/unit/objectLiteral.spec.ts @@ -63,3 +63,36 @@ test.each(['{x: "foobar"}.x', '{x: "foobar"}["x"]', '{x: () => "foobar"}.x()', ' util.testExpression(expression).expectToMatchJsResult(); } ); + +test("noSelf in object literal functions", () => { + // language=TypeScript + util.testFunction` + const foo: Record string> = { + method(a) { + return a; + }, + func: function(a) { + return a; + }, + arrow: (a) => { + return a; + } + }; + return [foo.method("a") ?? "nil", foo.func("b") ?? "nil", foo.arrow("c") ?? "nil"] + `.expectToMatchJsResult(); + // language=TypeScript + util.testFunction` + const foo: Record string> = { + method(a) { + return a; + }, + func: function(a) { + return a; + }, + arrow: (a) => { + return a; + } + }; + return [foo.method("a") ?? "nil", foo.func("b") ?? "nil", foo.arrow("c") ?? "nil"] + `.expectToMatchJsResult(); +}); From 3a7f8d045debb27fcac814289a11379a86461612 Mon Sep 17 00:00:00 2001 From: Benjamin Ye <24237065+enjoydambience@users.noreply.github.com> Date: Sun, 15 Aug 2021 11:54:41 -0700 Subject: [PATCH 2/3] Add isObjectLiteralExpression check on parent This check is absent in the internal typescript method --- src/transformation/utils/function-context.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/transformation/utils/function-context.ts b/src/transformation/utils/function-context.ts index 4d05458c3..b3e9ee2c5 100644 --- a/src/transformation/utils/function-context.ts +++ b/src/transformation/utils/function-context.ts @@ -114,7 +114,11 @@ function getSignatureDeclarations( return signatures.flatMap(signature => { const signatureDeclaration = signature.getDeclaration(); let inferredType: ts.Type | undefined; - if (ts.isMethodDeclaration(signatureDeclaration) && !getExplicitThisParameter(signatureDeclaration)) { + if ( + ts.isMethodDeclaration(signatureDeclaration) && + ts.isObjectLiteralExpression(signatureDeclaration.parent) && + !getExplicitThisParameter(signatureDeclaration) + ) { inferredType = context.checker.getContextualTypeForObjectLiteralElement(signatureDeclaration); } else if ( (ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) && From 8ab9d0cce7033b47b006a024f8757554b00c65b1 Mon Sep 17 00:00:00 2001 From: Benjamin Ye <24237065+enjoydambience@users.noreply.github.com> Date: Sun, 15 Aug 2021 12:14:41 -0700 Subject: [PATCH 3/3] Add more tests for PR --- test/unit/objectLiteral.spec.ts | 88 ++++++++++++++++++++++----------- 1 file changed, 59 insertions(+), 29 deletions(-) diff --git a/test/unit/objectLiteral.spec.ts b/test/unit/objectLiteral.spec.ts index 88d590afe..16e7582d0 100644 --- a/test/unit/objectLiteral.spec.ts +++ b/test/unit/objectLiteral.spec.ts @@ -64,35 +64,65 @@ test.each(['{x: "foobar"}.x', '{x: "foobar"}["x"]', '{x: () => "foobar"}.x()', ' } ); -test("noSelf in object literal functions", () => { - // language=TypeScript - util.testFunction` - const foo: Record string> = { - method(a) { - return a; - }, - func: function(a) { - return a; - }, - arrow: (a) => { - return a; +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 } - }; - return [foo.method("a") ?? "nil", foo.func("b") ?? "nil", foo.arrow("c") ?? "nil"] - `.expectToMatchJsResult(); - // language=TypeScript - util.testFunction` - const foo: Record string> = { - method(a) { - return a; - }, - func: function(a) { - return a; - }, - arrow: (a) => { - return a; + + const obj: FunctionContainer = { + method(a) { + return a + }, + func: function(a) { + return a + }, + arrow: (a) => { + return a + } } - }; - return [foo.method("a") ?? "nil", foo.func("b") ?? "nil", foo.arrow("c") ?? "nil"] - `.expectToMatchJsResult(); + + return [obj.method("a") ?? "nil", obj.func("b") ?? "nil", obj.arrow("c") ?? "nil"]; + `.expectToMatchJsResult(); + }); });