You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[TS/JS] making the emitted JS match the emitted TS ? #4951
I suggest making the emitted JS match the emitted TS.
For now, I can't use TS only because of some other errors in the emitted TS (yes, I could dig into those, but the emitted js works fine)
I would like to have d.ts for the public surface of my JS. When just creating d.ts files tsc can jump over the other errors.
What if we use the information known by the compiler to emit the .d.ts directly with the javascript? That would allow us to emit string enums, and other erased structures on the type declaration side without having to actually emit correct .ts.
Until Fable decides whether it wants something like this, you can use Fickl to write the .d.ts files straight from a JavaScript compilation, with no TypeScript output and no tsc step.
It takes the same arguments as fable. With --declaration, each .js file gets a .d.ts beside it. The declarations come from the same compilation: the file goes through the TypeScript code generator a second time, and only what the JavaScript actually exports is declared. So the union case helpers from this issue (Foo_Bar()), which only the TypeScript output has, never show up, and the .js is byte-for-byte what you get without the flag.
For example:
typeShape=| Circle ofradius:float| Empty
[<StringEnum>]typeColor=| Red
|[<CompiledName("dark-blue")>] DarkBlue
typePoint={ X:float; Y:float }letarea shape =match shape with| Circle r -> System.Math.PI * r * r
| Empty ->0.0
Erased, [<StringEnum>] and TypeScriptTaggedUnion unions are declared as type aliases, and files that only declare interfaces still get a .d.ts even though they have no JavaScript.
Class members are declared the way Fable compiles them to JavaScript: most F# members are module functions (Square__get_Side(sq)), and only interface implementations are methods.
fable-library-js ships with its own declarations, so the types imported from it (FSharpList, Option, int32, ...) resolve.
On Fable's JS test suite (75 files) every generated declaration type-checks with strict, apart from two cases that are the project's own: a [<Global>] class with no declaration anywhere, and an import of a hand-written .js file that has no types.
If this looks useful for Fable itself, I'm happy to send it upstream. It's a separate library that runs over Fable's existing Fable AST and Babel AST, with a small hook in the CLI.
Activity
shayanhabibi commentedon Oct 25, 2026
What if we use the information known by the compiler to emit the
.d.tsdirectly with the javascript? That would allow us to emit string enums, and othererasedstructures on the type declaration side without having to actually emit correct.ts.Would also skip a build step for you.
feat(js): write TypeScript declarations with --declaration
shayanhabibi commentedon Oct 25, 2026
Until Fable decides whether it wants something like this, you can use Fickl to write the
.d.tsfiles straight from a JavaScript compilation, with no TypeScript output and notscstep.It takes the same arguments as
fable. With--declaration, each.jsfile gets a.d.tsbeside it. The declarations come from the same compilation: the file goes through the TypeScript code generator a second time, and only what the JavaScript actually exports is declared. So the union case helpers from this issue (Foo_Bar()), which only the TypeScript output has, never show up, and the.jsis byte-for-byte what you get without the flag.For example:
gives this
Sample.d.ts(imports left out):Some details:
[<StringEnum>]andTypeScriptTaggedUnionunions are declared as type aliases, and files that only declare interfaces still get a.d.tseven though they have no JavaScript.Square__get_Side(sq)), and only interface implementations are methods.FSharpList,Option,int32, ...) resolve.strict, apart from two cases that are the project's own: a[<Global>]class with no declaration anywhere, and an import of a hand-written.jsfile that has no types.If this looks useful for Fable itself, I'm happy to send it upstream. It's a separate library that runs over Fable's existing Fable AST and Babel AST, with a small hook in the CLI.