fable-compiler / ts2fable

Parser of Typescript declaration files
http://fable.io/ts2fable/
Apache License 2.0
224 stars 34 forks source link

Assume single letter uppercase types names such as T, V, R, ... are abstract #273

Closed kristianmandrup closed 5 years ago

kristianmandrup commented 5 years ago

In cases such as this, T and V should be assumed to be abstract types. Current conversion logic requires me to painstakingly to go through all these and change them manually.

Can't really do a search and replace except perhaps a regexp taking account of multiple cases!? Perhaps this rule be one of multiple run options (could also available in the online UI).

type [<AllowNullLiteral>] dropRepeats =
    [<Emit "$0($1...)">] abstract Invoke: s: Flyd.Stream<T> -> Flyd.Stream<T>
    [<Emit "$0($1...)">] abstract Invoke: project: (T -> bool) -> (Flyd.Stream<T> -> Flyd.Stream<V>)

Should instead be converted to (note the 'T and 'V instead of T and V)

type [<AllowNullLiteral>] dropRepeats =
    [<Emit "$0($1...)">] abstract Invoke: s: Flyd.Stream<'T> -> Flyd.Stream<'T>
    [<Emit "$0($1...)">] abstract Invoke: project: ('T -> bool) -> (Flyd.Stream<'T> -> -> Flyd.Stream<'V>)

Thumbs up for the great project anyhow! I abandoned ReasonML due to the pain of having to do this manually and poor docs on how to write bindings in general. This is a massive boost!

Thanks :)

humhei commented 5 years ago

For currently version of ts2fable

https://fable.io/docs/interacting.html#foreign-interfaces image

In this use case ,

We have to do some dirty work,

For example, you can change your codes to obj -> obj(using box and unbox)

type [<AllowNullLiteral>] dropRepeats = 
[<Emit "$0($1...)">] abstract Invoke: obj -> obj 

And then directly invoke them

This may get worked..

Keep in mind you can always change interop codes to any codes and see the final compiled javascript codes in (webside https://fable.io/repl/) (localside https://www.npmjs.com/package/fable-splitter)

kristianmandrup commented 5 years ago

Cool, Thanks :) Let me know if I can do anything to help it out and make it even better. I will be using this tool a lot. I'm mostly a library author and "Bridger", bridging the "gaps" ;)

humhei commented 5 years ago

of course. Feel free to contribute to this repo, And ask me and @alfonsogarciacaro @MangelMaxime everything The recommand way for you to make a repair is: Add the origin code snippt(TypeImport.d.ts) to

image image

And then in fsfileTest.fs add your test image

image

Run the application

You will finally get TypeImport.fs

humhei commented 5 years ago

https://github.com/fable-compiler/ts2fable/blob/master/src/bridge.fs#L116 This is the soul of this app It transform typescript ast to fsharp ast And finnaly print them out image

kristianmandrup commented 5 years ago

Thanks :) Will have a look...

kristianmandrup commented 5 years ago

Ah yes, need to start looking into what JS is being generated to better understand it. I'd like to use my own lib bindings, such as for MithrilJS: https://github.com/fable-meiosis/fable-mithril/tree/master/src

However I can't yet figure out how to use the generated bindings as a consumer.

Won't let me use render tried many different ways, to open various "exposed" modules...

module MithrilApp

open Mag
// open Mag.Mithril
open Fable.Import.Browser

let root = document.body

// render root, "hello" |> ignore

printfn "Hello Mithril world"

Generated and slightly "polished"

// ts2fable 0.6.1
module rec Mag

open System
open Fable.Core
open Fable.Import.JS
open Fable.Import.Browser

type Hyperscript = Mithril.Hyperscript

type Attrs = obj
type State = obj

type [<AllowNullLiteral>] IExports =
    /// Manually triggers a redraw of mounted components. 
    abstract redraw: unit -> unit
    /// Renders a vnode structure into a DOM element. 
    abstract render: el: Element * vnodes: Mithril.Children -> unit
    // ...

module Mithril =

    type [<AllowNullLiteral>] Lifecycle<'Attrs, 'State> =
        /// The oninit hook is called before a vnode is touched by the virtual DOM engine. 
        abstract oninit: this: 'State * vnode: Vnode<'Attrs, 'State> -> obj option

    type [<AllowNullLiteral>] Hyperscript =
        /// Creates a virtual element (Vnode). 
        [<Emit "$0($1...)">] abstract Invoke: selector: string * [<ParamArray>] children: ResizeArray<Children> -> Vnode<obj option, obj option>

// ...

// let [<Import("*","module")>] RedrawService: Mithril.RedrawService.Static = jsNative            
// let [<Import("*","module")>] RenderService: Mithril.RenderService.Static = jsNative
// let [<Import("*","module")>] RequestService: Mithril.RequestService.Static = jsNative
// let [<Import("*","module")>] Stream: Mithril.Stream.Static = jsNative
// let [<Import("*","module")>] h: Hyperscript = jsNative
// let [<Import("*","module")>] Mithril: Mithril.Static = jsNative

// my experiment - trying to expose mithril render function!!!
let [<Import("*","module")>] MithrilRender: IExports = jsNative

Thanks for any advice.

PS: Too early for me to play with the engine ;)

humhei commented 5 years ago

let [<Import("*","module")>] MithrilRender: IExports = jsNative The ts2fable online version generated wrong module names

Your should place module to mithril(your npm module name) let [<Import("*","mithril")>] mithrilRender: IExports = jsNative

249

image Then use mithrilRender.redraw() to invoke it

kristianmandrup commented 5 years ago

Thanks, how about this one for seview?

module rec Seview
open System
open Fable.Core
open Fable.Import.JS

let [<Import("h","seview/react")>] svReact: Sv.IExports = jsNative
let [<Import("h","seview/preact")>] svPreact: Sv.IExports = jsNative
let [<Import("h","seview/mithril")>] svMithril: Sv.IExports = jsNative
let [<Import("sv","seview")>] seview: Sv.IExports = jsNative

type [<AllowNullLiteral>] IExports =
    abstract sv: transform: obj option * options: obj option -> obj option

https://github.com/foxdonut/seview#usage

You can optionally pass a second parameter to sv to indicate something other than className as the property to use for CSS classes. For example:

const h = sv(func, { className: "class" })

This would use the class property in the attrs to indicate the CSS classes.

So you need to write a snippet of code that you pass to sv to wire up seview with the virtual DOM library that you are using.

Mithril-Seview live usage example

import { h } from "seview@0.12.0/mithril/index.mjs";

const view = (state, actions) =>
  h(["div",
    ["span", "Temperature: ", state.temperature],
    ["span", { innerHTML: "&deg;C" }],
    ["div",
      ["button.btn.btn-primary", { onClick: () => actions.increment() }, "Increment"]
    ]
  ]);
module SeviewMithrilApp
open Seview

svMithril.sv ["h1"] // The field, constructor or member 'sv' is not defined.
kristianmandrup commented 5 years ago

I guess the problem here is:

type [<AllowNullLiteral>] IExports =
    abstract sv: transform: obj option * options: obj option -> obj option

module Sv =

    type [<AllowNullLiteral>] IExports =
        abstract prototype: obj

Which should be changed to a single IExports with the abstract sv function?

kristianmandrup commented 5 years ago

Yup, that fixed it, removing the Sv module and using the IExports type instead.

Now getting:

This expression was expected to have type
    'obj option * obj option'    
but here has type
    ''a list'

on let h = svMithril.sv ["h1"]

kristianmandrup commented 5 years ago

I will delete these comments shortly ;) sorry So it requires a tuple of two optional objects.