Hey,
I’m running into issues trying to type check an OO system I’ve made. I’m new to type checking with typeof(setmetatable(...)) and simply don’t understand what the problem is. I’d appreciate whatever correction anyone can give.
Simplified Code
ModuleA:
--!strict
type ModuleAType = {
__index: ModuleAType;
Foo: (self: TypeA) -> ();
new: (data: {[string]: string}) -> (TypeA);
}
local ModuleA: ModuleAType = {} :: ModuleAType
ModuleA.__index = ModuleA
export type TypeA = typeof(setmetatable({} :: {
A: string;
B: string;
}, {} :: ModuleAType))
function ModuleA:Foo()
print("Foo")
end
function ModuleA.new(data: {[string]: string}): (TypeA)
local self = {
A = data.A;
B = data.B;
}
setmetatable(self, ModuleA)
return self
end
return ModuleA
ModuleB:
--!nonstrict
local ModuleA = require(script.Parent.ModuleA)
type ModuleBType = typeof(setmetatable({} :: {
__index: ModuleBType;
Bar: (self: TypeB) -> ();
new: (data: {[string]: string}) -> (TypeB);
}, ModuleA))
export type TypeB = ModuleA.TypeA & typeof(setmetatable({} :: {
C: string;
}, {} :: ModuleBType)) -- Type Error: Cannot cast '{ }' into 'ModuleBType' because the types are unrelated
local ModuleB: ModuleBType = {} :: ModuleBType -- Type Error: Cannot cast '{ }' into 'ModuleBType' because the types are unrelated
ModuleB.__index = ModuleB
setmetatable(ModuleB, ModuleA) -- setmetatable should take a table
function ModuleB:Bar(): ()
print("Bar")
end
function ModuleB.new(data: {[string]: string}): (TypeB)
local self = ModuleA.new(data)
self.C = data.C
setmetatable(self, ModuleB)
return self
end
return ModuleB
There are three comments in ModuleB where I’m receiving type errors (lines 13, 15, and 17). When changing the typeof(setmetatable({...}, {} :: ModuleA)) on lines 5-9 of ModuleB to just the {...}, the errors disappear. I’m not sure if that’s the solution; in my mind the engine would then think that I’m not setting the metatable.
I know I’m explaining this terribly, so let me know if you need me to elaborate on anything.
There are many different ways to approach this kind of typechecking. Currently, my method is doing the following:
type Class = {
PropertyFoo: string,
PropertyBar: number,
FooBarMethod: (self: Class, printThis: string) -> (number),
}
local Class = {}
Class.__index = Class
function Class.new()
local self = (setmetatable({
PropertyFoo = "Foo",
PropertyBar = 123,
}, Class):: unknown):: Class --> slightly hacky, but works
return self
end
function Class.FooBarMethod(self: Class, printThis: string)
print(printThis)
return 1
end
-->> (results)
local NewClass = Class.new()
local thisIsANumber = NewClass:FooBarMethod("hello!")
print(thisIsANumber + 5) --> will not warn, prints 6
NewClass.PropertyFoo = "foooooo" --> this is okay
NewClass.PropertyFoo = 123 --> this is not okay
--<< (results)
@index_self@VegetationBush
Thank you both! I appreciate both of your replies, but I’ve opted for the first as it just seems more clean and intuitive to me.