ScalablyTyped / Converter

Typescript to Scala.js converter
https://scalablytyped.org
GNU General Public License v3.0
218 stars 43 forks source link

Support "as" props #125

Open littlenag opened 4 years ago

littlenag commented 4 years ago

Using converter addSbtPlugin("org.scalablytyped.converter" % "sbt-converter06" % "1.0.0-beta5") and attempting to generate bindings for react-bootstrap version 4.3.1 is failing. A sample error:

[error]  found   : typings.reactBootstrap.reactBootstrapMod.ReplaceProps[typings.reactBootstrap.reactBootstrapStrings.div,typings.reactBootstrap.reactBootstrapMod.BsPrefixProps[typings.reactBootstrap.reactBootstrapStrings.div] with typings.reactBootstrap.reactBootstrapTooltipMod.TooltipProps] with scala.scalajs.js.Object
[error]     (which expands to)  typings.std.stdStrings.Pick with scala.scalajs.js.Object | scala.scalajs.js.Any | scala.scalajs.js.Object | scala.scalajs.js.Any | typings.std.stdStrings.Pick with scala.scalajs.js.Object | scala.scalajs.js.Any with typings.react.AnonRefAny | _$1 | typings.std.stdStrings.Pick with _$1 forSome { type _$1 } with typings.react.mod.RefAttributes[scala.scalajs.js.Any] with typings.reactBootstrap.reactBootstrapMod.BsPrefixProps[typings.reactBootstrap.reactBootstrapStrings.div] with typings.reactBootstrap.reactBootstrapTooltipMod.TooltipProps with scala.scalajs.js.Object
[error]  required: typings.std.stdStrings.Pick with scala.scalajs.js.Object | scala.scalajs.js.Any | scala.scalajs.js.Object | scala.scalajs.js.Any | typings.std.stdStrings.Pick with scala.scalajs.js.Object | scala.scalajs.js.Any with typings.react.AnonRefAny | _$1 | typings.std.stdStrings.Pick with _$1 forSome { type _$1 } with typings.react.mod.RefAttributes[scala.scalajs.js.Any] with typings.reactBootstrap.reactBootstrapMod.BsPrefixProps[typings.reactBootstrap.reactBootstrapStrings.div] with typings.reactBootstrap.reactBootstrapTooltipMod.TooltipProps with scala.scalajs.js.Object [thread => 1202, project => client, ms => 85473, phase => build, id => react-bootstrap, flavour => Japgolly]
[error] PhaseRunner.scala:90 Failure, [react-bootstrap => Compilation failed] [thread => 1202, project => client, ms => 85562, phase => build, id => react-bootstrap]

However, when building with Slinky flavour the build succeeds. Best I can tell most of the issues are related to typings.std.stdStrings.Pick with ... in some way.

oyvindberg commented 4 years ago

Hopefully it'll work with with beta6, due in a few days. I'll be enabling expansion of mapped types for all libraries, so many instances of typings.std.stdStrings.Pick and friends will be replaced by generated interfaces with the proper rewrites applied.

Also are you sure there is a version 4.3.1? I tried to test it, but only found packages in other ranges

littlenag commented 4 years ago

D'oh. You are correct. I've updated the title to reflect the versions I was using.

littlenag commented 4 years ago

Btw, I also saw issues with @material-ui/core v4.9.5. I saw in the slinky demos usage of v3.9.3 so I ended up using that.

oyvindberg commented 4 years ago

Yeah, I'm aware of material-ui. It's the most complicated library I've come across, so it's a stretch goal to support it. 3.9.3 should work well enough for now

oyvindberg commented 4 years ago

I had a look if this was solved with beta6, which is almost ready.

It isn't, and the situation is exactly the same as with @material-ui/core. The props are polymorphic in a way which is difficult to represent in Scala. It's essentially this:

/* from @types/react. This is one of several moving parts, the other main complication is to compute `ref` */
    type ComponentProps<T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>> =
        T extends JSXElementConstructor<infer P>
            ? P
            : T extends keyof JSX.IntrinsicElements
                ? JSX.IntrinsicElements[T]
                : {};

type Props<Component <: React.ElementType = "div"> = {as?: Component; ...} & ComponentProps<Component>

This can be interpreted as a component which accepts all (or more probably, most) of the props which is possible to pass to a div, but where you can optionally replace div with an arbitrary react intrinsic string (a, div, etc) or an arbitrary component. And by doing this you replace all (or again, most) of the props!

still in typescript:

<Accordion onClick={(e) => console.warn(e)}/> // e has type MouseEvent<HTMLDivElement>
<Accordion as="a" onClick={(e) => console.warn(e)}/> // e has type MouseEvent<HTMLAnchorElement>
<Accordion as={MyComponent} onClick={(e) => console.warn(e)}/> // onClick might not even exist!

So to convert libraries like react-bootstrap and @material-ui/core we need two things:

I hope this clears up why it's not straightforward, and how we might get there. I'll hijack this issue to track "implementation of as-props", hope you don't mind. If anybody would like to look into possible encodings for this I'd be more than happy to work with you :)

littlenag commented 4 years ago

As an aside, why would the converter complete normally with the Slink flavour? Does Slinky enforce different invariants?

oyvindberg commented 4 years ago

Not sure about the specifics there, @littlenag . scalajs-react have a few more instances of type bounds, so it might be that. In any case it doesnt matter all that much whether it compiles if the result isn't all that useful anyway :)