salomonbrys
06/23/2022, 4:33 PMChildrenBuilder
(https://github.com/JetBrains/kotlin-wrappers/blob/master/kotlin-react/src/main/kotlin/react/ChildrenBuilder.kt), the ElementType<P>.invoke
function declares the generic bounds where P : Props, P : ChildrenBuilder
.
I can call this function inside a ChildrenBuilder
with, for example, ReactHTML.div.invoke {}
(here I put the invoke
to be explicit, even though it is not required).
Now, ReactHTML.div
is an IntrinsicType<HTMLAttributes<HTMLDivElement>>
, which is also an ElementType<HTMLAttributes<HTMLDivElement>>
, so the resolution kind of makes sense...
BUT I cannot find anywhere HTMLAttributes
implements ChildrenBuilder
, which should break the resolution of the ElementType<P>.invoke
function. The HTMLAttributes
interface implements AriaAttributes
, DOMAttributes
, PropsWithRef
, PropsWithChildren
, PropsWithClassName
, PropsWithStyle
, and Props
. How the hell can the Kotlin Compiler agrees that the where
clause of the ElementType<P>.invoke
function is satisfied ?!?!?
Furthermore, IntelliJ shows the invoke
"this" hint as ChildrenBuilder & HTMLAttributes<HTMLDivElement> & Props
, when, according to the invoke
function signature, it should simply display HTMLAttributes<HTMLDivElement>
. Where is the ChildrenBuilder
coming from ? What black magic is that ?!?
Oh, and when I ask IntelliJ to explicit the type argument to invoke
, it can't! It puts Any
, which clearly breaks the where
clause. If I try to set myself the type argument with div.invoke<HTMLAttributes<HTMLDivElement>>
, then IntelliJ tells me that "Type argument is not within its bounds", which I fully agree with!
Help me, please, I'm losing it 🙃 !Sergei Grishchenko
06/23/2022, 4:39 PMElementType
is contravariant by P
generic because props is inputsalomonbrys
06/23/2022, 4:42 PMP
?Sergei Grishchenko
06/23/2022, 4:43 PMP
is generic to represent props
external interface ElementType<in P : Props>
salomonbrys
06/23/2022, 4:44 PMHTMLAttributes<HTMLDivElement>
...Sergei Grishchenko
06/23/2022, 4:44 PMSergei Grishchenko
06/23/2022, 4:46 PMElementType
is FunctionComponent
, FunctionComponent
is opaque alias to (Props) -> ReactNode?
so as you may heard all functions are covariant by return type and contravariant by parameterssalomonbrys
06/23/2022, 4:48 PMSergei Grishchenko
06/23/2022, 4:51 PMFunctionComponent
is declared approximately this way
type FC<P> = (props: P) -> ReactNode
Sergei Grishchenko
06/23/2022, 4:52 PMFunctionComponent
can be invoked, but the rest contract is the samesalomonbrys
06/23/2022, 4:53 PMHTMLAttributes<HTMLDivElement>
(since it does not implement ChildrenBuilder
, and IDEA can't explicit the type argument to the invoke
function, so what can it be ?Sergei Grishchenko
06/23/2022, 4:57 PMP
.
Are you asking about this signature?
operator fun <P> ElementType<P>.invoke(
block: @ReactDsl P.() -> Unit,
) where P : Props,
P : ChildrenBuilder
salomonbrys
06/23/2022, 4:59 PMSergei Grishchenko
06/23/2022, 5:08 PMthis: IntrinsicType<HTMLAttributes<HTMLDivElement>>
your P
is HTMLAttributes<HTMLDivElement>
but because ElementType
is contravariant by P
, clause where P : Props, P : ChildrenBuilder
it is not upper bound, it is lower bound, you can pass in invoke
function any ElementType
whose generic type is Props & ChildrenBuilder
or more abstract, so you can pass there ElementType<Props>
, ElementType<ChildrenBuilder>
or ElementType<HTMLAttributes<HTMLDivElement>>
Sergei Grishchenko
06/23/2022, 5:11 PMProps & ChildrenBuilder
is subtype for Props
and for ChildrenBuilder
Sergei Grishchenko
06/23/2022, 5:22 PMinterface Parent
interface Child : Parent
typealias Task = (Child) -> Unit
fun doWork(task: Task) {
task(object : Child {})
}
fun main() {
val parentTask = { _: Parent -> }
val childTask = { _: Child -> }
doWork(childTask)
doWork(parentTask)
}
salomonbrys
06/24/2022, 12:52 PMSergei Grishchenko
06/24/2022, 12:59 PMinterface Props
interface PropsAndChildrenBuilder : Props
typealias ElementType = (PropsAndChildrenBuilder) -> Unit
fun createElement(elementType: ElementType) {
elementType(object : PropsAndChildrenBuilder {})
}
fun main() {
val regularFC = { _: Props -> }
val syntaticFC = { _: PropsAndChildrenBuilder -> }
createElement(regularFC)
createElement(syntaticFC)
}
Sergei Grishchenko
06/24/2022, 1:00 PMSergei Grishchenko
06/24/2022, 1:04 PMcreateElement
receives only strange ElementType
with PropsAndChildrenBuilder
arg, but in fact when you pass ElementType<Props>
it is also fine for createElement
Sergei Grishchenko
06/24/2022, 1:07 PMIntrinsicType<HTMLAttributes<HTMLDivElement>>
to invoke
, it seems than invoke
receives only something like that IntrinsicType<HTMLAttributes<HTMLDivElement> & ChildrenBuilder>
but in fact IntrinsicType<HTMLAttributes<HTMLDivElement>>
is also ok for invoke
paramsalomonbrys
06/24/2022, 1:10 PMSergei Grishchenko
06/24/2022, 1:12 PMsalomonbrys
06/24/2022, 1:12 PMsalomonbrys
06/24/2022, 1:13 PMsalomonbrys
06/24/2022, 1:13 PMSergei Grishchenko
06/24/2022, 1:25 PM(T) -> R
in Kotlin refers to internal type
interface Function1<in T, out R>
, so you can declare you own types with contravariance,
so even ElementType<in P: Props>
is not lambda type, it is contravarint by it's P
generic, you can treat ElementType
as "something abstract that receives props P
as input"