dillonkearns / elm-typescript-interop

Generate TypeScript declaration files for your elm ports!
BSD 3-Clause "New" or "Revised" License
165 stars 13 forks source link

Missing `export` #10

Closed sporto closed 6 years ago

sporto commented 6 years ago

When I generated my TS I got this:

export namespace Elm {
  namespace Admin {
    export interface App {
      ports: {
        toJsUseToken: {
          subscribe(callback: (data: string) => void): void
        }
        toJsSignOut: {
          subscribe(callback: (data: null) => void): void
        }
      };
    }
    export function init(options: {
      node?: HTMLElement | null;
      flags: { apiHost: string; tokenData: { name: string; email: string; role: string }; token: string };
    }): Elm.Admin.App;
  }
}

Note the missing export in namespace Admin.

So when I try to declare something as Elm.Admin I get

[ts] Namespace '"../Admin/index".Elm' has no exported member 'Admin'.

Thanks

sporto commented 6 years ago

hmm, adding export manually doesn't fix the error. I don't quite understand how TS works.

sporto commented 6 years ago

Will close as export is not the issue apparently.

dillonkearns commented 6 years ago

Hey @sporto, yeah it's a bit weird, but exporting the leaf nodes is all that's needed, and then they will show up under that namespace.

As for trying to annotate something as Elm.Admin, this doesn't make sense if I'm understanding you correctly. That would be like annotating something in Elm as Json.Decode instead of Json.Decode.Value. Json.Decode is like the namespace, and Value is the exposed (or exported) type. So it would only make sense to annotate a type as Elm.Admin.App. But you shouldn't have to do that since calling init will return something with that annotation, right?

Maybe I'm missing what you're trying to do here, if so please clarify. Thank you!

sporto commented 6 years ago

I have several Elm apps Admin, Public, Investor.

I have a function that expects one of those for example Admin.:

function doSomeSetupAndInit(Admin) {
    ... stuff
    Admin.init({node...})
}

I was trying to add a signature to that Admin in argument. So don't I get why it doesn't make sense. In JS everything is an object, so you can get hold of Elm.Admin and pass this around.

dillonkearns commented 6 years ago

Gotcha @sporto, thanks for clarifying! For some reason I assumed that you were trying to annotate your argument (something like const app: Elm.Admin.App). But what you're trying to do makes perfect sense.

Does it work if you do something like this?

function doSomeSetupAndInit(admin: Elm.Admin.App) {
}
sporto commented 6 years ago

(admin: Elm.Admin.App) is fine. But I don't have an App at this point. I want to call init in this function.

So try to pass Elm or Elm.Admin is the issue. e.g. function(elm: Elm) As TS complains with Cannot use namespace 'Elm' as a type.

dillonkearns commented 6 years ago

Hello @sporto, this looks a little odd, but it's the only way I could figure out how to do it.

function doSomeSetupAndInit(parameter: typeof Elm) {
}

If you can find a better way to write the .d.ts file that makes it easier to annotate, I'm totally open to it. But from what I gather, I think the structure of the TypeScript declaration file here is pretty standard, it's just an oddity that you can't use namespaces as an annotation.

By the way, I figured that out by declaring a variable const elmThing = Elm and then hovering over elmThing. It told me the type was typeof Elm.

sporto commented 6 years ago

I haven't done much TS, so I don't know if this is this a thing, but isn't it possible to created the declarations just using Interface?

export Interface Elm {
   Admin: Interface {
       App: Interface { 
            ....
       }
   }
}

So you can annotate any of the intermediate objects?

dillonkearns commented 6 years ago

@sporto I created #13 to track this issue for better visibility.

If you're able to manually tweak your .d.ts file to get it working the way you want then I can use that for the code generation easily. I'll play around with it a bit when I get a chance.