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
Copy file name to clipboardExpand all lines: docs/blog/2023/2023-04-20-Better_Typed_than_Sorry.md
+76-2Lines changed: 76 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,7 +14,7 @@ If you are a Fable user you may have noticed that we released, quite quietly, Fa
14
14
15
15
> If you haven't upgraded yet, we recommend you do it soon: there are no breaking changes and you [only need to update a few packages](../2022/2022-09-28-fable-4-theta.html#packages-updated-for-fable-4).
16
16
17
-
With Fable 4.1 we are happy to announce one of those targets is ready for prime-time! Thanks to the effort of [ncave](https://github.com/ncave) who has been leading the effort to add type annotations to JS output (in spite of my initial reluctance) for a long time, TypeScript compilation started to become a real possibility. We have been working together in the last month and TypeScript generated code is almost on par now with JS so we have decided to bump the status from beta to stable 🚀
17
+
With Fable 4.1 we are happy to announce one of those targets is ready for prime-time! Thanks to the work of [ncave](https://github.com/ncave) who has been leading the effort to add type annotations to JS output (in spite of my initial reluctance) for a long time, TypeScript compilation started to become a real possibility. We have been working together in the last month and TypeScript generated code is almost on par now with JS so we have decided to bump the status from beta to stable 🚀
18
18
19
19
Wait a moment, compiling to TypeScript? Does this mean I should compile all my JS apps to TypeScript from now on for extra type-safety? No, we don't recommend that. In fact, you probably shouldn't. Many Fable libraries already emit JS code that TypeScript may not accept happily and we cannot control that. TypeScript compilation is intended for integrating F# into existing TypeScript and to write libraries that can be published to npm and consumed with a type-safe API. This is in line with the focus we are giving to the new language targets: instead of trying to do everything in F#, we will concentrate our efforts on having a great experience with [Domain Programming](https://fsharpforfunandprofit.com/ddd/) in all platforms. This means spending less time writing bindings for native libraries and instead working on generating nice code that can be easily consumed.
20
20
@@ -82,7 +82,7 @@ c.line(data)
82
82
})
83
83
```
84
84
85
-
> A similar effect can be reached with [anonymous records](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/anonymous-records).
85
+
> A similar effect can be achieved with [anonymous records](https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/anonymous-records).
86
86
87
87
### Exporting members
88
88
@@ -265,6 +265,80 @@ test(MyUnion_Bar(5))
265
265
test(MyUnion_Baz())
266
266
```
267
267
268
+
### TypeScript Tagged Unions
269
+
270
+
Even if F# unions can be used in a typed-safe manner from TypeScript, when you build a public API you may want your unions to feel more "native". And it happens that TypeScript does have the concept of a [discriminated union](https://www.typescriptlang.org/docs/handbook/typescript-in-5-minutes-func.html#discriminated-unions): which is a union of JS objects where all of them share a common property that has the function of the "tag". Fable can compile F# unions to TypeScript tagged unions thanks to the `TypeScriptTaggedUnion` attribute. This was a feature contributed by [cannorin](https://github.com/cannorin) and since **Fable 4.1.3** it has been updated to support TypeScript annotations. You define the name of the tag property in the attribute, and the name of each case will become the value of the tag. It is also important that you use **named fields** for each case. Let's see an example:
271
+
272
+
```fsharp
273
+
[<TypeScriptTaggedUnion("type")>]
274
+
type Command =
275
+
| Take of fromIndex: int * toIndex: int
276
+
| Edit of text: string
277
+
| Save
278
+
279
+
let test = function
280
+
| Take(fromIndex, toIndex) -> printfn $"Taking from {fromIndex} to {toIndex}"
281
+
| Edit(text) -> printfn $"New text: {text}"
282
+
| Save -> printfn "Saving"
283
+
284
+
Take(5, 7) |> test
285
+
```
286
+
287
+
In TypeScript it becomes:
288
+
289
+
```ts
290
+
exporttypeCommand=
291
+
| { type:"take", fromIndex:int32, toIndex:int32 }
292
+
| { type:"edit", text:string }
293
+
| { type:"save" }
294
+
295
+
exportfunction test(arg:Command):void {
296
+
switch (arg.type) {
297
+
case"edit": {
298
+
toConsole(`New text: ${arg.text}`);
299
+
break;
300
+
}
301
+
case"save": {
302
+
toConsole(printf("Saving"));
303
+
break;
304
+
}
305
+
default: {
306
+
toConsole(`Taking from ${arg.fromIndex} to ${arg.toIndex}`);
307
+
}
308
+
}
309
+
}
310
+
311
+
test({
312
+
type: "take",
313
+
fromIndex: 5,
314
+
toIndex: 7,
315
+
});
316
+
```
317
+
318
+
By default the case names will change to camel case, but as with `StringEnum` you can pass a `CaseRules` argument to the attribute to control this, as well as using `CompiledName` in a case:
Types decorated with `TypeScriptTaggedUnion` have similar limitations to `Erase` and `StringEnum`, that is, because the type has no actual representation in the JS runtime, you cannot use reflection, do type testing or attach interfaces (you can still use instance and static members from F#). It is recommended to use the special attributes only to interact with native code and not in your everyday F# programming.
340
+
:::
341
+
268
342
## Compilation options
269
343
270
344
In order to compile to TypeScript, you only need to pass the `--lang ts` option to Fable tool, but let's have a look at [the extra options in the compostjs example](https://github.com/alfonsogarciacaro/compost/blob/e783ed687bc19887f02aa0b071585a1a152c8956/package.json#L7-L11):
0 commit comments