Reinforced.Typings - a library for the automatic generation of TypeScript-tipping and not only

I wrote a small but useful library for TypeScript and ASP.NET MVC lovers. I would very much like to talk about it - perhaps to some developer on the aforementioned combination of technologies (and possibly to the whole team) it will make life much easier. There is no time to write full documentation in English. In general, you need to approach it consciously, with feeling, sense and arrangement. So I decided to write an article on Habrahabr, and right now, under the cut, I will do a brief review and show what magic you can do this thing. Welcome.

Lyrical introduction


In general, the idea of ​​tight integration of the Back-End and Front-End on the .NET stack for a long time and I am strongly excited, which in turn even resulted in an attempt to write an entire translator from C # in JavaScript. I will not say that no results were achieved - my translator successfully translated several C # classes into JavaScript and eventually went into suspended animation and rethinking the architecture. Someday I will come back to him. Mandatory.

But, nevertheless, the current tasks in the projects had to be somehow solved. And the current tasks in almost any web-project, as a whole, are quite typical and, I'm not afraid of the word, boring. Now, with your permission, I am abstracting from all sorts of complex, but long-understood matters, such as using Automapper, collecting an IoC container and rustling in a database using EF / NH / anything, and will switch closer to the front-end. So - at the junction of the backend and frontend there are also many boring and typical tasks (sarcasm). Specifically, requests to the server for JSON with data, their display and the performance of various operations by AJAX. Reinforced.Typings (which is exactly what I called my little help) will bring a bit of fun to this kingdom of despondency, simplification of typical tasks, getting rid of template code and a bit more consistency.

Ever since Microsoft gave TypeScript to us, writing client-side JavaScripts has become much more comfortable. TypeScript brought a sense of typability and compilation to where it was lacking. If you have not tried it yet, then be sure to try it (this is not advertising, no). You can, of course, argue a lot about the issue of "to be or not to be" TypeScript in your specific project, but let's lower the discussion and go to "- Get closer to the body! - as Guy de Maupassant said."

Practical example


So, let's look at 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 a browser.

What do we usually do to solve this problem? Right. We do POCO order models and a controller method that 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 unnecessary. 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 redundant glue code, as well as creating a view, TypeScript file, connecting it to the page, installing jQuery from NuGet - you can do all this without me. I emphasize the essence (once again I remind you that this TypeScript code):

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 fine here. Except that fundamentally from JavaScript, this construction is no different. We get a piece of JSON from the server in which some object and we, relying on the fact that it has the ClientName, Address and other fields, display it in a div. It does not sound very stable. If some unfortunate junior removes, from the ViewModel and C # code, say, the ClientName field (or renames it for junior refactoring purposes), then all the places on the frontend where such a construction is used will turn into detonators and will wait the arrival of the tester. Well, or end user - and then someone will be so lucky. What to do? The answer is obvious - since we use TypeScript, you can write tipping 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 we have become somewhat more comfortable - 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 peacefully. Yes, and writing taips for all ViewModel-s ... hands ... at night ... And if there are hundreds of them in the project? And if thousands? The prospect is, frankly, so-so.

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

PM > Install-Package Reinforced.Typings

We notice in the root of the project a new file Reinforced.Typings.settings.xml. It is documented in sufficient detail and to rewrite everything that is stated in it here, I don’t see the point (unless of course someone from the audience is not doing so well with English), leaving this to the common people. I will change only one parameter - this is the path to the file where the generated tipping will lie. In my project, it’s like this:

$(ProjectDir)Scripts\app\Server.ts

After that, I go to the modelka’s code and add only two lines of code - using on Reinforced.Typings.Attributes and the [TsInterface] attribute above the modelka ’s class itself. Something like this:

using Reinforced.Typings.Attributes;
[TsInterface]
public class OrderViewModel
{
	// в самом коде модельки ничего не изменилось
}

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

Server.ts Content
//     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;		
	}
}


Do you see? Wonderful. Now the task of writing taips for the ViewModel of the entire project does not seem such a terrible thing, does it? Yes, and deleting-renaming properties ViewModel is no longer a tragedy: the next time you build the project, the tippings are regenerated and the TypeScript code that is tied to them simply stops collecting, as you can see for yourself.

I think this practical demonstration of the main possibility can be completed by leaving more complex examples in the following articles and moving on to a dramatic conclusion.

A dramatic conclusion and a little about that


Reinforced.Typings actually supports a lot of things. Here is a short list:
  • Automatic tiling of delegates, descendants of IEnumerable and IDictionary (if you use them as properties)
  • Taiping for enums and classes (although he cannot automatically translate the body of methods into TypeScript. But you can do it yourself - see below)
  • Types with complex types - Reinforced.Typings understands that you are using another exported type in the class and automatically puts the full-qualified name of the type used in that place. Otherwise - tactfully uses any
  • You can scatter the generated code into different files (class-per-file) using the appropriate configuration parameter
  • Can add ///to generated files using the assembly attribute [TsReference]. And in the case of class-per-file, reference is automatically added to neighboring files
  • You can generate .d.ts files instead of the usual .ts code (there are some differences in the syntax)
  • Cherry on the cake - each attribute has the CodeGeneratorType property, in which you can specify the inheritance type from Reinforced.Typings.Generators.ITsCodeGenerator <> (as an option, inherit from the existing generator) and make your own generation of template TypeScript code for anything . In this way, you can get to the automatic creation of knockout's ViewModel directly from the server-side ViewModel code. In the project at my current place of work, I overloaded the code generator for controller actions and generated a js-th glue code for many methods, calling the corresponding controller method with the specified parameters. Such methods return q-shny promise (simply because I love Q.js). I’ll talk about this in the next post

Of the minuses: Automatically generate bodies of methods of classes Reinforced.Typings can not - it works through Reflection . Well, I also want to note some problems in a situation where server entities present the correct TypeScript-code, but the already generated code contains a semantic error (for example, the field is deleted). Due to the features of the TypeScript assembly (it is the very first in the entire project), you cannot rebuild the project and generate the correct tippings that will fix the error until you correct the error manually. But I'm working on it. The magic of MSBuild works wonders.

Still on the project, as mentioned above, there is very little documentation (this article, but readme on the 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 am already recruiting a student technical writer and I will make a normal Reinforced.Typings Best Practices book in wiki format documentation.

That's all for today. If you have any questions, write in the comments. I will try to answer.

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

UPD example : Second article on Reinforced.Typings, which provides much more details

Also popular now: