Open muelli opened 6 years ago
Now with three elements in the record, the generated d.ts files are still comprehensible. But if the Player model grows, the generated file contains a lot of repetitive elements.
Hello @muelli, could you help me understand what problem this addresses? I'm trying to understand the value here to help me prioritize things better. Especially because this is a non-trivial task, I'd like to get some sense of what use cases this helps with.
You mention making the .d.ts
files more comprehensible. But that doesn't seem necessary to me, it's just an internal detail that's hidden from the user, right? Is there anything else that this would help with. I would love to know if there is a specific problem that you've encountered yourself that would be improved by a feature like this.
I appreciate you starting the discussion, thanks!
We have just adopted this project and it has been really helpful so far. Thanks for your awesome work @dillonkearns
On our project it would be helpful to have the port arguments generated as exported types so that we can use them in the port handling functions in typescript. Right now we are are defining our own types that matches the type for the port argument.
You mention making the
.d.ts
files more comprehensible. But that doesn't seem necessary to me, it's just an internal detail that's hidden from the user, right? Yes and no.
For me, I want to continue working with these types in other functions.
Currently, a change in the Elm type rightfully causes a change in the .d.ts
file.
But it also causes a change in all the other function signatures that consume that type. That change I have to perform manually. It seems avoidable to me having to touch all my functions taking a Player
if all I do is adding a field to the type.
If there was an Interface, I could simply consume that in my functions.
In fact, that's what I'm doing manually now and I have to watch out for the .d.ts
and my Interface not going out of sync too much.
And because I have to ship the .d.ts
file (because parsing my Elm codebase fails, so I cannot generate the file on the fly. Even if I could, I wouldn't know how to actually make it happen with the Webpack setup I have) I tend to review changes in it every so often. And with big types, it's hard to see changes in the type definitions. But I appreciate that this is a fringe case that shouldn't exist.
I've came up with a fairly decent utility type to use as a workaround
type ExtractElmPortData<
TProp extends keyof Elm.Main.App['ports']
> = Elm.Main.App['ports'][TProp] extends {
subscribe(callback: (data: infer TData) => void): void
}
? TData
: Elm.Main.App['ports'][TProp] extends {
send(data: infer TData): void
}
? TData
: unknown
type RecipeOut = ExtractElmPortData<'receiveRecipe'>
type RecipeIn = ExtractElmPortData<'saveRecipe'>
and the .d.ts generated by elm-typescript-interop...
export namespace Elm {
namespace Main {
export interface App {
ports: {
receiveRecipe: {
send(data: { slug: string; name: string; ingredients: { name: string }[] }): void
}
saveRecipe: {
subscribe(callback: (data: { slug: string; name: string; ingredients: { name: string }[] }) => void): void
}
};
}
// ...
}
}
Cool.
This makes types for the argument to subscribe
and send
, right?
It is possible to further split the types up? I.e. to have a type for name
or ingredient
?
The answer is: yes.
type Ingredient = RecipeIn['ingredients'][0]
This is very amazing. Thanks for pointing these types out.
I have quite a few ports and I think I would enjoy if elm-typescript-interop would write out my Elm type aliases along with the actual signature of the port.
Let me explain a little.
If have a model Player (adapted for brevity):
and I have many ports sending or receiving that player. Now with three elements in the record, the generated d.ts files are still comprehensible. But if the Player model grows, the generated file contains a lot of repetitive elements.
elm-typescript-interop could generate something like
and use that instead in the function definitions.
Then the question is when to actually do that. One idea is to have annotations, e.g. in comments: