As of writing this post I’ve found that there is a question on StackOverflow showing an interest to this topic - How to reuse existing C# class definitions in TypeScript projects.
As always there are two ways of solving the problem - use existing solution or write a new one.
There are at least two tools are available - TypeLite and T4TS. Everything is good with these tools but when it came to customization it turned out that you need to decorate the classes with some fancy attributes or code transformation functions. This means that you should mix in the requirements like module/property naming convention to the classes that are not even aware of existence of some client project that indirectly depends on them.
You can call me a purist but hey, why would I need to keep the metadata required for one project in another? And why should I complicate things and instruct the team working with a server side of how to decorate the classes with attributes that are needed by other team? Simple things should be simple. I just want my C# classes/structs/enums to be transformed to the TypeScript interfaces/classes/enums.
From my experience when it comes to codegeneration most of the time you will not find the “ready for use” solution that will 100% satisfy you. Best case is that you’ll find something that is simple and easy to change.
So I’ve chosen the second path - hack my own solution. For DTOs I’ve decided to write a code generator based on T4 Text Templates and reflection. And since I have a TypeScript based project my code generation templates are producing TypeScript code. Why TypeScript? For me, the main reason is compile time errors. I like that I can have classes and interfaces which usage will be checked by compiler at development time and I will see the mistakes before I run the app. Also it is worth to mention that TypeScript supports almost all features of the the ECMAScript 6 which is also good because investing time in TypeScript now I will be up to date with the latest standard available.
I also strongly believe that it is critically important to run code generation on every build and have no auto-generated things committed in a source control. This approach minimizes the probability of mistakes made by engineers (yes, I’ve had experience when warnings like
// This code was auto-generated were ignored).
Since I’m going to generate classes I have to describe the metadata I need. This would be the name of the interface/enum and a list of its members:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
MetadataHelper class is the heart of solution - it will extract the data needed for codegeneration using reflection:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
To be able to parameterize and run codegeneration on every build I’m using preprocessed T4 templates (for more information on topic please refer to the Oleg Sych’s Understanding T4: Preprocessed Text Templates blog post). Preprocessed template generates a partial class that I’ll be able to extend with metadata I need:
1 2 3 4 5 6 7
And the actual template. It also contains a helper method that translates .NET types to the corresponding TypeScript type names.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
And the usage is very simple. I’ve created a console application which could be launched for example on CI server during the build.
1 2 3 4 5 6 7 8 9 10 11 12
As you can see with a very little effort I’ve got a working and open to any customizations codegenerator. As always the code from this post is available on Github. Feel free to clone and adjust to your needs.
Keep the code simple!