#Why does intersection of object types not make union of same-name properties?

24 messages · Page 1 of 1 (latest)

novel wigeonBOT
#
trusktr#0

Preview:```ts
///////////////////////////////////////////////////

export type Constructor<T = object, A extends any[] = any[], Static = {}> = (new (...a: A) => T) & Static

export function Constructor<T = object, Static = {}>(Ctor: Constructor<any>): Constructor<T> & Static {
...```

sterile carbon
#

For some reason, you can see the error there happens in the HasError subclass.

Further below we can see how it is that it forms a union string[] | (string[] & Record<...>) | undefined which to me doesn't seem to make sense.

Why is the property not string[] | Record<...> | undefined? Isn't that what you would expect?

sleek marten
#

Intersection you can think of as the overlap (or, ahem, intersection) of two types

#

(string | number) & (number | boolean) is number

#

The overlap of string[] | Record<string, AttributeHandler> and string[] is basically just string[]

#

Although technically you could have an object that satisfies both string[] and Record<string, AttributeHandler> making that a possible option

muted pine
sleek marten
#

Parens everywhere

sterile carbon
#

@muted pine @sleek marten Interesting. Well as you can see above the comment that says "re-create the intersection", I have not written that intersection anywhere. The intent is to allow the author of a subclass to provide a static observedAttributes field that can be either string[] or Record<string, AttributeHandler>. How might we achieve that in this case?

muted pine
#

i hadn't looked at the playground before, but you do have that intersection at PossibleCustomElementConstructor & T

#

PossibleCustomElementConstructor => { observedAttributes?: string[] | undefined }
typeof LumeElement => { observedAttributes?: string[] | Record<string, AttributeHandler> | undefined }
SomeMixin(LumeElement) has T as typeof LumeElement, so the 2 above values are being intersected

mathematically/technically/imperatively, intersection is distributive over union
logically, you'd need { observedAttributes?: string[] | undefined } to satisfy both (ie, an intersection)
typeof LumeElement is a supertype, not a subtype, of PossibleCustomElementConstructor

#

if the subclass can have string[] | Record<string, AttributeHandler>, then the parent also has to have that, just how subtyping works

sterile carbon
#

I see what you mean about subtyping. Hmmm. Maybe I just have to re-org, f.e. two properties, or update PossibleCustomElementConstructor to include that Record type.
Thanks!

sterile carbon
#

@muted pine I can't seem to get this to work out. Even if I update the type of PossibleCustomElementConstructor to match with the type of the other property that is being merged, I still get this in a subclass:

Class field 'observedAttributes' defined by the parent class is not accessible in the child class via super.

which is wrong, because this is a static class field we're talking about, and those are always accessible with super (fields and accessors are always installed on the class where they are defined, not this)

Seems like this super error is a bug?

#

^ that's with the old type. And here's with the fixed type, still having the super access problem:

muted pine
#

(sorry for the ghost ping, mistested something)

#

this works fine, could you make a mcve?

novel wigeonBOT
#
that_guy977#0

Preview:```ts
class A {
static x = 0
}

class B extends A {
static {
super.x
}
}```

sterile carbon
# muted pine this works fine, could you make a mcve?

Maybe I'm somehow causing an edge case bug to happen, but haven't been able to make a simple repro yet.

For now what I did to work around was something like this:

const Super = TheBaseClass

class MyClass extends Super {
    static override observedAttributes = [...(Super.observedAttributes || []), 'slot']
}
#

Working on a repro

sterile carbon
#

@muted pine ok I made an mcve. Looks like the problem is due to using two mixins at different levels of the hierarchy, where both mixins use the same pattern to be defined:

novel wigeonBOT
#
trusktr#0

Preview:```ts
type Constructor<
T = object,
A extends any[] = any[],
Static = {}

= (new (...a: A) => T) & Static

function Constructor<T = object, Static = {}>(
Ctor: Constructor<any>
): Constructor<T> & Static {
return Ctor as unknown as Constructor<T> & Static
}

class LumeElement extends HTMLElement {
...```

sterile carbon
#

If you remove any one of the two mixins (InitialBehaviors or CompositionTracker), then problem goes away.

If you remove one of the two observedAttributes definitions in LumeElement or PossibleCustomElementConstructor then the problem also goes away.