Self Does Not Get Types With Luau

Issue Type: Other
Impact: Moderate
Frequency: Constantly
Date First Experienced: 2021-04-23 08:04:00 (-04:00)
Date Last Experienced: 2021-04-23 08:04:00 (-04:00)

Reproduction Steps:
Inserting the following script into a Script will not have type information for the self variable.

--!strict

local Account = {}
Account.__index = Account

function Account.new(name, balance): Account
    local self = {}
    self.name = name
    self.balance = balance

    return setmetatable(self, Account)
end

function Account:deposit(credit)
    self.balance += credit
end

function Account:withdraw(debit)
    self.balance -= debit
end

export type Account = typeof(Account.new("", 0))

local account: Account = Account.new("Alexander", 500)

This is taken from the Luau docs and has some modifications.

Expected Behavior:
I expect the self variable to have types.

Actual Behavior:
The self variable does not receive any types.

Workaround:
Giving the self variable a type.

11 Likes

Thanks for the report! It looks like the actual underlying issue is that the type alias Account is given some other type. We’ll take some time to fix this.

5 Likes

Did this get resolved? I can’t tell if I’m crazy for having memories of self working correctly during parts of the last two years. The typing on self hasn’t actually worked for me in some time, and luau seems to get lost very easily in very common module patterns when metatables or __index are invlolved.

I’ve been burning ridiculous numbers of hours trying to find reliable patterns that allow Script Analysis and auto completion to work with code unless I explicitly define all the types up front.
Very simple classes will generate inferred type definitions where

local Module = {}
Module.__index = Module

function Module:Foo()
end
function Module:Bar()
end

will end up giving you a type like

{
  <a>Foo: (self:a)->(),
  <b>Bar: (self:b)->()
}

with no recognition that self a, self b, and the module all share the same type and properties.
Even with --!strict on, Script Analysis understands self to be type any, so it doesn’t generate warnings.

I have code where I work around that basically the way @catgirlin_space did, or I may have the first line of every class method be
local self:SpecificClassType = self

Except now you have the problem of either needed to define the type ahead of time, or you have
type SpecificClassType = typeof(SpecificClassTable)
in your code somewhere, and that creates a new set of luau script analysis problems

I keep running into writing a bunch of code in a way that makes luau happy and me maybe even happy, but stops working (to satisfy luau) soon after.

It’s not an exaggeration that I’ve burned hundreds of hours changing my coding style over and over to get type inference, auto complete, and script analysis to work, bending code structure around what might work for luau.

I’d have gotten a lot more done if I gave up on that 2 years ago, and moved my lua editing to visual studio. Roblox Studio really really has come very far in the last few years. luau has come very far. It’s impressive, but I’m still wondering when luau is going to pay off and make for an actually smooth coding/testing/debug experience.

I’m still re-inventing lua module definition patterns over an over to try to get luau to work right.

3 Likes

Did this get resolved yet ?
Type suggestions doesn’t shows when writing self inside a method declaration of a table/class/metatable.
And this is really horrible.

Edit: here a workaround

local class = {}
class.__index = class

type this = typeof(class)&{ -- typeof for methods
	myattribute:number
}

function class.new()
	local self = {}
	self.myattribute = 10
	setmetatable(self, class)
	return self
end

function class.DoSomething(self:this)
	self. -- Type annotations shows correctly (DoSomething, new and myattribute)
end

very late edit: you can obviously use colons for method calls instead of a dot because it eats the first argument.

3 Likes

Having this issue too, and the workarounds seem to require me to ditch using colons in functions :sob:

Expected result (in Slot.new() → Slot)


Notice how I’m able to access the same value in the constructor function, its type is Slot, yet self is any in the function parameters.

Actual result (in Slot:PushToHotbar())

image
Notice how the type of self is any instead of Slot, which is what Slot.new returns.

Type Definitions

image

8 Likes

this is still a problem over a year later

4 Likes

We are still considering improving this. Thanks for your patience!

The best workarounds for this are either function-contained methods or defining methods with .method(self, ...). It makes OOP feel more obtrusive than it already was.

local class = {}
class.__index = class

type Class = {
    print: (self: Class, ...any) -> ()
}

function class.new(): Class
    return setmetatable({}, class)
end

function class.print(self: Class, ...: any)
    print(...)
end

return class :: {
    new: () -> Class
}

This defines a very obvious problem with Luau OOP and it doesn’t feel good to use. It gets even worse with inherited types on the class itself.

local class = {}
class.__index = class

type Class<T> = {
    a: T,
    set: (a: T) -> ()
}

function class.new<T>(a: T): Class<T>
    return setmetatable({a = a}, class)
end

function class.set(self: Class<T>, a: T) -- ! T isn't inherited, should be any, providing T would expect it to be passed again on method call
    self.a = a
end


return class :: {
    new: <T>(a: T) -> Class<T>
}