📜 ⬆️ ⬇️

Reinforced.Typings is a library for automatically generating TypeScript-taipings and not only

I wrote a small but useful library for TypeScript and ASP.NET MVC lovers. I would very much like to tell you about it - maybe some developer on the aforementioned combination of technologies (and possibly the whole team) will make life much easier. There is no time to write full documentation in English. It generally needs to be approached intelligently, with feeling, plainly and constellation. So I decided to write an article on Habrahabr, and right now, under the cut, I will do a brief overview and show you what magic you can do with this thing. Welcome.

Lyrical introduction


In general, the idea of ​​tight integration of the Back-End and Front-End on the .NET stack, which in turn even resulted in an attempt to write a whole translator from C # in JavaScript, has long and firmly aggravated me. I won't say that no results were achieved - my translator successfully translated several C # classes into JavaScript and eventually went into hibernation and rethinking the architecture. Someday I will come back to him. Required.

But, nevertheless, the current tasks in the projects had to somehow be solved. And the current tasks in almost any web project are, on the whole, quite typical and, I’m not afraid of that word, boring. Now, I, with your permission, abstract from any complicated, but long-understood matters, such as using Automapper, picking up an IoC container and rustling in the database using EF / NH / something, and switch closer to the frontend. So - at the junction of the backend and the frontend, there are also many boring and typical tasks (sarcasm). Specifically, server requests for JSON with data, their display and the performance of all sorts of different AJAX operations. Reinforced.Typings (and that is how I called my little help) will bring a bit of fun to this realm of dejection, simplifying typical tasks, getting rid of the template code and a bit more consistency.

Since Microsoft gave us TypeScript, writing client-side JavaScript has become much more comfortable. TypeScript brought a sense of typability and precompilability to where it was missing. If you have not tried it yet, be sure to try it (this is not an advertisement, no). You can, of course, argue a lot on the question of “to be or not to be” TypeScript in your particular project, but let's skip the discussion and move on “- Closer to the body! - as Guy de Maupassant said”.
')

Practical example


So, let's consider a simple but fairly common example - you need to make a request to the server, get information about the order and display it in some way on a page in the browser.

What do we usually do to solve this problem? Right. We make the POCO order model and the controller method, which will return its instance wrapped in JSON. Here they are, our heroes (as you understand, I will remove the extra code to save space):

Modelka
public class OrderViewModel { public string ItemName { get; set; } public int Quantity { get; set; } public decimal Subtotal { get; set; } public bool IsPaid { get; set; } public string ClientName { get; set; } public string Address { get; set; } } 


Controller method
 public ActionResult GetOrder(int orderId) { var orderVm = new OrderViewModel() { // ...   ... }; return Json(orderVm, JsonRequestBehavior.AllowGet); } 


Here everything is more or less clear and comments, I think, are superfluous. Let's switch to the client. To be extremely clear, I will use jQuery for ajax requests, but if necessary, you can replace it with something of your own. As before, I omit the unnecessary glue code, as well as creating a view, a TypeScript file, connecting it to the village, installing jQuery from NuGet - you can do this without me. I emphasize the very essence (once again I remind you that this code is in TypeScript):

TypeScript code
 private btnRequest_click() { $.ajax({ url: '/Home/GetOrder?orderId=10', success:this.handleResponse }); } private handleResponse(data: any) { var text = `${data.ClientName}, ${data.Address} (${data.ItemName}, ${data.Quantity})`; $('#divInfo').text(text); } 


Everything is beautiful here. Except that fundamentally from JavaScript, this construct is no different. We get a piece of JSON from the server, in which some object and we, counting on the fact that it has the fields ClientName, Address and others - output it to a div. It does not sound very stable. If any unfortunate junior removes from the ViewModel and C # code, say, the ClientName field (or renames it for junior refactoring), then all the places on the frontend that use this design will turn into detonators and wait the arrival of the tester. Well, or end user-a - here it is someone as lucky. What to do? The answer is obvious - since we use TypeScript, then we can write the tapping for this particular ViewModel and rewrite the code like this:

 private handleResponse(data: IOrderViewModel) { var text = `${data.ClientName}, ${data.Address} (${data.ItemName}, ${data.Quantity})`; $('#divInfo').text(text); } 

Yes, now it has become somewhat more comfortable for us - we have insured against access to an undeclared field. But the situation with the junior renaming the field personally does not allow me to sleep. Yes, and writing taiping for all ViewModel-her ... hands ... at night ... And if there are hundreds of them in the project? And if thousands? Perspective, frankly, so-so.

This is where Reinforced.Typings comes into play and the solution of the problem begins radically in a different way. So, open the PM-console (well, or anyone is comfortable - you can do it through the graphical interface) and set:

 PM > Install-Package Reinforced.Typings 

We notice in the project root a new Reinforced.Typings.settings.xml file. It is documented in sufficient detail and I do not see any point in rewriting everything stated in it here (unless of course someone from the audience is not so bad with English), giving it to the habra people. I will change just one parameter - this is the path to the file where the generated tapping will go. In my project, he is like this:

  <RtTargetFile>$(ProjectDir)Scripts\app\Server.ts</RtTargetFile> 

After that, I go to the code of the model and add just two lines of code - using Reinforced.Typings.Attributes and the [TsInterface] attribute over the model itself. Like this:

 using Reinforced.Typings.Attributes; [TsInterface] public class OrderViewModel { //        } 

After that, I rebuild the project (I rebuild it) and manually add it to Scripts \ app \ generated according to the Server.ts configuration. He is already in the specified folder - just not added to the project. Let's open Server.ts and see what is in it:

Content Server.ts
 // This code was generated by a Reinforced.Typings tool. // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. module Reinforced.Typings.Samples.Simple.Quickstart.Models { export interface IOrderViewModel { ItemName: string; Quantity: number; Subtotal: number; IsPaid: boolean; ClientName: string; Address: string; } } 


You see? Wonderful. Now the task of writing taipings for the ViewModel to her entire project does not seem such a terrible thing, does it? And the deletion-renaming of properties ViewModel is no longer a tragedy: at the next build of the project, the typings will be regenerated and the TypeScript code, which is tied to them, will simply cease to be assembled, which you can see for yourself.

I think that a practical demonstration of the main possibility can be completed on this, leaving more complicated examples for the following articles and moving on to a dramatic conclusion.

Dramatic conclusion and a little bit about this and that


Reinforced.Typings actually supports a lot of things. Here is a short list:

Of the minuses: Automatically generate the body methods of classes Reinforced.Typings can not - it works through Reflection . Well, I would also like to note some problems in the situation when server entities represent the correct TypeScript code, but the already generated code contains a semantic error (for example, a field has been deleted). Due to the features of TypeScript assembly (it is going to be the very first in the entire project), you will not be able to rebuild the project and generate correct taipings that will correct the error until you correct the error manually. But I'm working on it. The magic of MSBuild works wonders.

Also, according to the project, as mentioned above, there is very little documentation (this article, yes readme on github). BUT! XMLDOC is very detailed and the settings file is commented. So, I think for the first time should be enough. And there I’m already enrolling a student technician and doing a normal book Reinforced.Typings Best Practices documentation in a wiki format.

In general, that's all for today. If there are any questions - write in the comments. I will try to answer.

Reference to the project:
Reinforced.Typings on Github
Reinforced.Typings on NuGet
Full code of the considered example

UPD : The second article on Reinforced.Typings, which provides much more details

Source: https://habr.com/ru/post/266899/


All Articles