[New Type Solver] fails to infer valid instantiation for generic with intersection

most minimal repro:

--!strict

type little_frame = Frame
type BIG_FRAME = {something: Frame} & Frame
type OTHER_BIG_FRAME = Frame & {something: Frame}
type context<O> = {_object: O}

local little = Instance.new("Frame") :: little_frame
local big = Instance.new("Frame") :: BIG_FRAME
local other_big = Instance.new("Frame") :: OTHER_BIG_FRAME

local little_context: context<little_frame> = {_object = little}
local big_context: context<BIG_FRAME> = {_object = big}
local other_big_context: context<OTHER_BIG_FRAME> = {_object = other_big}

local function fn<O>(p: context<O>)
end

fn(little_context) -- OK
fn(big_context) -- TypeError: 
--[[
    No valid instantiation could be inferred for generic type parameter O. It was expected to be at least:
        Frame & { something: Frame }
    and at most:
        Frame & { something: Frame }
    but these types are not compatible with one another.
--]]
fn(other_big_context) -- OK

print(big.something) -- OK
print(other_big.something) -- TypeError: Type 'Frame & { something: Frame }' does not have key 'something'

I came across this while working on my ui system, which i use a global typedef for player_gui (which i automatically compile via startergui), but the repro does not mandate global typedefs and can be done in a standalone sourcecontainer.

Expected behavior

big_context with BIG_FRAME is the one that has proper typing and lets me autocomplete big.something so it is the one I generally use. I don’t really want to have to use two typedefs; one for generics and one for general indexing.

intersections shouldn’t really be the way to set child-parent relationships as it would not really be possible to dictate that if x.y then y.Parent = x using this; is there a proposed solution for this yet? how soon can we expect this? IIRC Studio does carry this context; if so, how does Studio do it internally?

If it were up to me some sort of overload for & should be implemented for Instances and Instances only.

1 Like

Thank you for the bug report! Agreed, this seems like a bug. We’re aware of general issues when trying to intersect tables and types from the Roblox API (like you are doing). We’ll try to keep you in the loop as we figure out a fix.

intersections shouldn’t really be the way to set child-parent relationships as it would not really be possible to dictate that if x.y then y.Parent = x using this; is there a proposed solution for this yet? how soon can we expect this?

At least for now, the Roblox API is the only place that can define these relationships. Instead of trying to define parent-and-child relationships, you can compose data structures, e.g.:

type BigFrame = {
  a_frame: Frame,
  something: Frame
}

type OtherBigFrame = {
  another_frame: Frame,
  something: Frame
}
2 Likes

OK, thanks.
For now, a feasible workaround is to just drop the generic and opt for a more literal fn definition as fn(p: context<any>).

Hello! Sorry for the delay, but this looks like it has since been fixed. The following snippet, in Roblox Studio, has no errors:

--!strict

type little_frame = Frame
type BIG_FRAME = {something: Frame} & Frame
type OTHER_BIG_FRAME = Frame & {something: Frame}
type context<O> = {_object: O}

local little = Instance.new("Frame") :: little_frame
local big = Instance.new("Frame") :: BIG_FRAME
local other_big = Instance.new("Frame") :: OTHER_BIG_FRAME

local little_context: context<little_frame> = {_object = little}
local big_context: context<BIG_FRAME> = {_object = big}
local other_big_context: context<OTHER_BIG_FRAME> = {_object = other_big}

local function fn<O>(p: context<O>)
end

fn(little_context) -- OK
fn(big_context) -- TypeError: 
--[[
    No valid instantiation could be inferred for generic type parameter O. It was expected to be at least:
        Frame & { something: Frame }
    and at most:
        Frame & { something: Frame }
    but these types are not compatible with one another.
--]]
fn(other_big_context) -- OK

print(big.something) -- OK
print(other_big.something) -- TypeError: Type 'Frame & { something: Frame }' does not have key 'something'

I am going to mark this issue as fixed but feel free to reach out if you run into any more problems!