"Type ... does not have key ..." error after applying table inheritance

It’s been a while since I’ve worked with inheritance and immediately I’m running into one headache of an issue. What am I doing wrong?

Context

Based on the example classes below, I want Derived to inherit the properties & functions of Base through the use of metatables.
Each of these classes reside in their own ModuleScript instance and their type must be referable through a <module>.Class syntax.

Problem

Accessing a member of the Base class within a function defined on Derived through the self argument results in a “Type does not have key” error, despite autocomplete’s ability to show the correct member and type. How come?

(Here it correctly suggests the 'instance' property I'm trying to reference)

Repro

Base
--!strict

const Base = {}
Base.__index = {}

export type Class = setmetatable<{
    read instance: Instance,
}, typeof(Base)>

function Base.new(instance: Instance): Class
    return setmetatable({
        instance = instance,
    }, Base)
end

function Base.ChangeName(self: Class, name: string): ()
    error("Override required.")
end

return Base
Derived
--!strict

const Base = require(script.Parent.Base)

const Derived = setmetatable({}, Base)
Derived.__index = Derived

export type Class = setmetatable<Base.Class & {
    -- ...add new members here.
}, typeof(Derived)>

function Derived.new(instance: Instance): Class
    return table.freeze(setmetatable(Base.new(instance), Derived))
end

function Derived.ChangeName(self: Class, name: string): ()
    self.instance.Name = name -- TypeError: Type 'Class' does not have key 'instance'
end

return Derived

this has no such error

function Derived:ChangeName(name: string): ()
	self.instance.Name = name
end

which is weird, because it’s the same.

That’s a Luau quirk – the solver can’t resolve the self argument by itself through table functions defined through the : syntax.

Quoted from the official docs

…While Luau is capable of inferring a decent chunk of this code, it cannot pin down on the types of self when it spans multiple methods.

You’ll see that through using :, autocomplete suggestions completely fail.
The suggested workaround is to explicitly define self through the . syntax.

3 Likes

I’ve filed a bug report for this seeing as to how this post is fading into irrelevance with no solution, with the verdict leaning towards a bug.