Sorry about that, it’s a type cast which is used on expressions to refine types when the inferred type is too broad.
So in your case, with a table, when you’re doing type t = typeof(setmetatable({}, {})), with no type casting, you’re really saying that both the table and the metatable can both have any type of entry with any type of key and any type of value, with no set interface.
However when you type cast as in my post above, you’re saying that the table will contain the entries in object_props, and the metatable will have a __index metamethod which points to a table that will have the members of object_methods.
Ah okay that’s no problem, you should be able to export the type (export type object = ...) then in your modules that will inherit your class, you can just use an intersection type to inherit from the child class.
now say we want our class cool_object to inherit object, we’d just do
local object = require(path.to.object)
type cool_object_props = {
cool: boolean;
}
type cool_object_methods = {
doSomething: (self: cool_object) -> ()
}
-- here we get the intersection type of cool_object and object using &
type cool_object = typeof(setmetatable({} :: cool_object_props, {} :: { __index: cool_object_methods })) & object.object
Kind of the whole syntax part. I don’t know too much about this, but I’m not sure if that much is needed to just change an autofill from recognizing a function or method.
Sorry if I sound a little rude, it’s a little late for me lol
I believe you do have to do this much though because you’re going to run into an unrelated type error when you try to set the type of a table with metatable to be the type of a normal table without a metatable
Another option would be to switch to nocheck mode by doing --!nocheck at the top of your script but then you don’t get any of the other warnings that would point out possible issues in your code
Can you show the relevant parts of your scripts (namely when and what you’re returning, when you’re requiring the module, and when you’re creating and indexing the object)?
--object--
local object = {}
object.__index = object
--composition--
local entity = require(replicated_storage.framework_shared.classes.abstract.entity)
--types--
type _object = typeof(setmetatable(entity.new(script.Name), object)) & {
func: () -> () -- an attribute which holds a function that is passed through with .new
}
--class init function--
function object.new(input_bind_list: {computer: Enum.KeyCode?, console: Enum.KeyCode?}, output_bind_list: {computer: Enum.KeyCode?, console: Enum.KeyCode?}, func: () -> ())
local self: _object = setmetatable(entity.new(script.Name), object)
self.func = func
--hidden code
return self
end
--class methods--
function object:fire()
self.func()
end
return object
The other end is a local script insignificant code occurring above the screenshot I sent.
Oh I must have misunderstood, I was thinking object was going to be the base class that other classes inherit, not the other way around.
I believe the issue stems from the fact that calling setmetatable on a table that already has a metatable won’t update the table’s metatable in the type checker.
So to get around this you’d have to update your types in the entity module first:
local entity = {}
entity.__index = entity
-- important parts below:
type entity_props = { -- this has all the properties in `entity`
entityProp: boolean
}
type entity_methods = { -- this has all the `entity` class methods
entity_method: (self: entity_type) -> ();
--__index: entity_methods; -- optionally __index can also go here
}
-- below is where we set the __index metamethod for the type checker
-- export type will export this type so they're accessible elsewhere
export type entity_type = typeof(setmetatable({} :: entity_props, {} :: { __index: entity_methods }))
function entity.new(name: string): entity_type
local self = setmetatable({}, entity)
return self
end
function entity:entity_method()
end
return entity
Then in your object module:
--composition--
local entity = require(script.Parent.entity)
--object--
local object = setmetatable({}, entity) -- in this case, setmetatable allows `object` to inherit the `entity` class methods
object.__index = object
--types--
-- properties that `object` will have
type object_props = {
func: () -> ()
}
-- again, class methods
type object_methods = {
fire: (self: object_type) -> ();
}
-- finally set the object to be a type:
export type object_type = typeof(setmetatable({} :: object_props, {} :: { __index: object_methods })) & entity.entity_type
--class init function--
function object.new(input_bind_list: {computer: Enum.KeyCode?, console: Enum.KeyCode?}, output_bind_list: {computer: Enum.KeyCode?, console: Enum.KeyCode?}, func: () -> ()): object_type
local self: object_type = setmetatable(entity.new(script.Name), object)
self.func = func
--hidden code
return self
end
--class methods--
function object:fire()
self.func()
end
return object
Finally, as a test:
local object = require(script.Parent.object)
local obj = object.new()
obj:fire() -- ok; auto complete shows up
obj:entity_method() -- ok; auto complete shows up
obj.func() -- ok; auto complete shows up
local p = obj.entityProp -- ok; auto complete shows up
Also, my OOP system is a little weird. I have one base abstract class. All it has is an “entity id” and a few other things such as the destroy method. All classes inherit from “entity”, but from there, it works with composition, and the classes can use each other as components.