-
-
Notifications
You must be signed in to change notification settings - Fork 184
Description
I've found a couple of issues with transpiling functions and dealing with 'this'.
Example 1:
interface Foo {
bar(s: string): void;
}
const o: Foo = {
bar: (s: string) => print(s)
}
o.bar("foo");local o = {bar = function(s) return print(s) end};
o:bar("foo"); --prints "table: 00709330"Here, the colon is mistakenly used because the interface declares the function as a method, but the implementation creates it as an arrow function. This is a tough issue to solve since there's no way to know for sure which operator to use at the call site.
Example 2:
class C {
private prop = "foo";
public outer() {
const o = {
prop: "bar",
innerFunc: function() { print(this.prop); },
innerArrow: () => print(this.prop)
};
o.innerFunc(); //Should print "bar"
o.innerArrow(); //Should print "foo"
}
}
let c = new C();
c.outer();C = C or {}
C.__index = C
function C.new(construct, ...)
local self = setmetatable({}, C)
self.prop = "foo"
if construct and C.constructor then C.constructor(self, ...) end
return self
end
function C.constructor(self)
end
function C.outer(self)
local o = {prop = "bar",innerFunc = function()
print(self.prop);
end
,innerArrow = function() return print(self.prop) end};
o.innerFunc(); --Incorrectly prints "foo"!
o.innerArrow();
end
local c = C.new(true);
c:outer();Here the inner function incorrectly captures self from the outer method.
I suspect there's probably a lot of other similar edge cases around. They all boil down to inconsistencies with how JS handles 'this' binding.
As I understand it, in JS, all functions have a 'this', which is whatever was on the left side of the dot when called. The 2 exceptions are arrow functions and stand-alone functions (ones with no left-side to bind). Arrow functions capture the 'this' from their outer scope and stand-alone functions have the global scope (window) as their 'this'.
I believe the solution is to re-work how functions are transpiled overall to mirror that behavior:
- All functions should be given an initial 'self' parameter
- Arrow functions should name this first parameter with a dummy name ('_') so they capture the self of their outer scope
- All functions (except stand-alone) should be called with colon syntax
- Stand alone functions should (maybe?) pass _G as their first parameter
With this setup, all lua functions would have a self that mirrors 'this' from the equivalent JS. Plus, there would be no need to decide whether to use dot or colon as all calls would use colon.
The one case this doesn't handle is functions from external sources that need to be called with dot syntax. A decorator would probably be the way to handle this. I know that was shot down before (see #108), but I think it's worth reconsidering.
Thoughts?