“type ‘string’ could not be converted into ‘class’”
Example code:
--!strict
type classData = {
b:buffer;
len:number;
}
local metatable = {
__concat = function(self:class,str:string):class
buffer.writestring(self.b,self.len,str)
self.len+=#str
return self
end;
}
export type class = typeof(setmetatable({}::classData, metatable))
--returns a long string
local new = function():class
return setmetatable({
b = buffer.create(100_000::number);
len = 0;
}::classData,metatable)::class
end
local class = new()
class..="Hello"
BUT casting it to “any” absolutelly solves linting proving my theory that it is a problem for custom defined metamethods
local class:class|any = new()
class..="Hello"
I tried using setmetatable<classData,typeof(metatable)> but result is the same.
1 Like
Hi,
Late to the show here, but I was digging into this problem myself and it’s worth noting that the order of __tostring’s args do actually change around depending on how the concatenation is set up, specifically whether or not it’s followed by any further concatenation; regardless, checking which is the table type and which is the string type has been the most straightforward approach for me.
Here’s how I apply it in a basic setting:
type MyData = {
Summary: string
}
local MT = {
__concat = function(pre: any, post: any): string
if (type(post) == "table") then
return (pre or "") .. (post :: MyType).Summary
end
return (pre :: MyType).Summary .. (post or "")
end
}
--[[ Or shorter if that's your thing
local MT = {
__concat = function(pre: any, post: any)
return (pre.Summary or pre) .. (post.Summary or post)
end
}
]]
type MyType = setmetatable<MyData,typeof(MT)>
local myTestInstance: MyType = setmetatable({
Summary = "Foo"
},MT)
print(myTestInstance) -- This is where a __tostring implementation would be more appropriate
print("--> " .. myTestInstance)
print(myTestInstance .. " <--")
print("--> " .. myTestInstance .. " <--")

Incidentally, my typechecker would not stop complaining until I had __tostring written in this manner.
Most examples online just do some variety of table concatenation/basic prints and don’t bother with all the different ways you can concatenate something (though these are hardly edge cases…), but naturally when working with custom types you want a little more control over what actually gets printed.
Anyway, I’m a little confused about how you’re looking to use buffers in this example, but here is a proposed adjustment nonetheless:
local metatable = {
__concat = function(pre:any,post:any): class
local class: class = if type(pre) == "table" then pre else post
local str = if type(pre) == "string" then pre else post
buffer.writestring(class.b,class.len,str)
class.len+=#str
return class
end;
}
Let me know if this helps! Sorry for the necro but having found an important detail it seemed notable enough to post.