Strictmode Autocomplete Issues

Before replying, please remember strictmode is enabled for all scripts in this post.

I am attempting to implement class inheritance with strictmode type checking and running into some issues, specifically with Studio’s native autocomplete.

I have the following module (InheritedObject):

--!strict

local Object = require(script.Parent)

local _module = setmetatable({}, Object)
_module.__index = _module

export type InheritedObject = typeof(setmetatable({} :: {
	newAttr: number
}, _module)) & Object.Object

function _module.new(public: boolean, new: number): InheritedObject
	local self = setmetatable(Object.new(public) :: InheritedObject, _module)
	self.newAttr = new
	
	self:newMethod()
	
	return self
end

function _module:newMethod(): ()
	print("New method called")
end

return _module

The problem is calling a method of the new class (NOT the class it is inheriting from):

self:newMethod()

The IDE does not throw any errors or warnings, and it works perfectly fine if I playtest it. However, the autocomplete does not show :newMethod() as a valid method of self.

inheritance_error_example

It only shows the methods it inherited.

I thought, maybe the issue is the autocomplete has no idea newMethod actually exists, since it’s defined later in the script. So I defined a type just for the module…

type InheritedObjectProvider = typeof(setmetatable({} :: {
	__index: InheritedObjectProvider,

	new: (public: boolean, new: number) -> InheritedObject,
	newMethod: (self: InheritedObject) -> ()
}, Object))

…which just didn’t work. :woman_shrugging:

Since this isn’t a critical issue because the module runs perfectly fine, I can just deal with it if nothing can be done. However, I would prefer for the autocomplete to… work, though? Any ideas?

Thanks in advance,
Fizzitix

quick bump, any ideas at all??

another bump
i can provide additional information if necessary

I don’t have a full grasp on how the type system works yet so I can’t say for sure why your method doesn’t work, but I can show you how I handle inheritance with the type system. I haven’t really experienced any issues with it so far.

type BaseClassProperties = {
    Foo: number,
}

type BaseClassPrototype = {
    BaseFunctionA: (BaseClass) -> (),
    BaseFunctionB: (BaseClass) -> (),
}

type BaseClass = typeof(setmetatable(
    {} :: BaseClassProperties,
    {} :: { __index: BaseClassPrototype }
))

type ChildClassProperties = {
    Bar: string
}

type ChildClassPrototype = {
    ChildFunctionA: (ChildClass) -> (),
    ChildFunctionB: (ChildClass) -> (),
}

type ChildClass = BaseClass & typeof(setmetatable(
    {} :: ChildClassProperties,
    {} :: { __index: ChildClassPrototype }
))

I don’t include the __index field for either the properties types or prototype types. I only include it when using setmetatable.

Creating children instances is basically the same as you have it except I’ll do something like this instead:

local ChildClass = setmetatable(BaseClass.New() :: ChildClass, { __index = ChildClassPrototype })

In my experience, I’ve noticed that as soon as I have the “prototype” object also be the metatable with __index pointing to itself, it completely breaks the type system for me. I never really knew how to fix that.

This produces some… strange behavior.

The methods inherited from the base class, such as :publicMethod() and :privateMethod(), do not work with the inherited object. However, they appear on the list of methods in the autocomplete menu, and do not raise any errors or warnings in strictmode.

I believe this occurs because, since you create a new BaseClass object with the new ChildClass object, the typechecker assumes the ChildClass object has access to those methods. But since we actually change the metatable of the BaseClass object to form the ChildClass object, the new object does not actually have access to the previous methods.

I assume this works just fine for you, though. Have you tried using it in strictmode? To what extent have you tested and used it?

(P.S. atp this whole thing just seems like a bug with the typechecker, especially in strictmode)

Yea, I only use strict mode on my projects and this works completely for me. Both the typechecker and actual runtime code work fine. I’ve not tested it beyond a single parent and child class but I imagine it’d work fine multiple inheritance levels deep.

By “do not work”, do you mean that you get runtime errors mentioning how you’re attempting to call a nil value or how the functions can’t be found in the table?

Sorry, I wasn’t that clear on the actual code I use outside of that small snippet. You’re right that we do change the metatable of the BaseClass object when creating it, but ChildClassPrototype also has a metatable that points to BaseClass so the child class will still have access to the base class methods if they aren’t found in the child class prototype.

You’re doing something similar when you set the _module metatable to Object. Here’s what I do:

-- I've also typecasted ChildClass with :: ChildClassPrototype here. 
-- At the moment, I can't remember whether that is beneficial or not, but it worked either way
local ChildClass = {} 

-- ... Assume I've implemented all the required methods written in the ChildClassPrototype type

local ChildClassPrototype: ChildClassPrototype = ChildClass
ChildClassPrototype = setmetatable(ChildClassPrototype, { __index = BaseClassPrototype })
1 Like

This one.

That makes sense. I’ll give it a go and mark solution if it works.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.