c# typescript
by Leonardo Carreiro
莱昂纳多·卡雷罗(Leonardo Carreiro)
Who has never experienced the situation where you have to fix a bug and at the end you find out that the error on the server was a missing field coming from a HTTP request? Or an error on the client, where your Javascript code was trying to access a field that doesn’t exist on the data that came in an HTTP response from the server? A lot of times, these problems are caused just by a different name for this field between the code on the client and the server.
谁曾遇到过必须修复错误的情况,最终您发现服务器上的错误是来自HTTP请求的缺少字段? 还是客户端发生错误,您的Javascript代码试图访问服务器HTTP响应中的数据中不存在的字段? 很多时候,这些问题仅是由客户端和服务器上的代码之间的此字段名称不同引起的。
Everyone who works both on the back-end and front-end of a web application has to query and process data on the server-side and then return these data to be consumed by the client-side of the application. No matter how many layers your architecture is divided into, you always will have the edge between the server and the client, where the HTTP requests and responses carry on the data between those two sides in both directions.
在Web应用程序的后端和前端都工作的每个人都必须在服务器端查询和处理数据,然后将这些数据返回给应用程序的客户端使用。 无论您的体系结构分为多少层,您始终将在服务器和客户端之间处于边缘,HTTP请求和响应在这两个方向之间在两个方向上进行数据传输。
And this is not just about the bugs with different names — no one can remember the entire data structure of all the entities of the application. When you are writing code, it’s common to type a .
(or -&
gt; or
[“). If you don’t write a wrong name there, you stop and ask yourself “What the heck was the name of that field?”. After you spend some time trying to remember, you give up and choose the most boring path. You take your mouse and start looking for the file where you define all those fields that you need to access.
而且,这不仅是具有不同名称的错误-没有人会记住应用程序所有实体的整个数据结构。 在编写代码时,通常会键入a .
(或-&
gt; or
[“)。 如果您在此处没有输入错误的名称,则停下来问自己“该字段的名称到底是什么?”。 在花了一些时间来记住之后,您放弃并选择了最无聊的道路。 用鼠标开始查找文件,在其中定义需要访问的所有那些字段。
The boring part of writing code is when you cannot figure out by yourself what is the right code that you need to write.
编写代码的无聊部分是您无法自己弄清楚需要编写什么正确的代码。
Sometimes it doesn’t hurt to just google it and you find a Stack Overflow answer with the code there, ready to be copied. But when you have to search for this answer inside your project, a big project, where the code that defines the data structure that you have to access is in a file that wasn’t written by you…the time you spend on this path can be one or two orders of magnitude bigger than the time spent just writing the right name.
有时,只用谷歌搜索并不会带来伤害,您会在其中找到包含代码的Stack Overflow答案,随时可以复制。 但是,当您必须在项目(一个大型项目)中搜索此答案时,其中定义您必须访问的数据结构的代码位于您未编写的文件中……您花在此路径上的时间可能会比只写正确的名字要花费的时间大一两个数量级。
When we used to write just plain old Javascript, we didn’t have an option to avoid this boring path in these situations. But then, at the end of 2012, Anders Hejlsberg (the father of the C# language) and his team created TypeScript. Their mission was to make it easier to create large Javascript projects that scale.
当我们过去只写普通的旧Javascript时,在这些情况下,我们没有选择避免这种无聊的路径的选择。 但是后来,在2012年底, Anders Hejlsberg (C#语言之父)和他的团队创建了TypeScript。 他们的任务是使创建可扩展的大型Javascript项目变得更加容易。
The funny part is that, while this new language was a superset of Javascript, its objective was to allow you to do only a subset of things that you used to do with Javascript. It added new features like classes, enums, interfaces, parameter types, and return types.
有趣的是,虽然这种新语言是JavaScript的一个超集 ,它的目的是让你做只是事情的一个子集 ,你使用JavaScript做。 它添加了新功能,例如类,枚举,接口,参数类型和返回类型。
But it also removed possibilities, even things that weren’t too bad, like passing a number as a parameter to document.getElementById()
, and using the *
operator with a number and a numeric string as operands. You cannot count with implicit type conversions anymore, you have to be explicit and use .toString()
or parseInt(str)
when you do want a type conversion. But the best thing that you can’t do anymore is to access a field that doesn’t exist in an object.
但这也消除了可能性 ,即使情况还算不错,例如将数字作为参数传递给document.getElementById()
,并使用带有数字和数字字符串的*
运算符作为操作数。 您不能再进行隐式类型转换了,您必须是显式的,并且在确实要进行类型转换时使用.toString()
或parseInt(str)
。 但是,您无法做的最好的事情就是访问对象中不存在的字段。
So, when a problem is resolved, a new one often takes its place. And here the new problem was the duplication of code. People started replacing the DRY principle (Don’t Repeat Yourself) by the WET principle (Write Everything Twice).
因此,解决问题后,通常会替换一个新问题。 这里的新问题是代码重复。 人们开始用WET原则(两次写所有东西)代替DRY原则(不要重复自己)。
It is a good practice to use different classes in different layers, for different purposes, but it is not the case here. If you have three layers (A -> B -> C), you shouldn’t have specific data structures for each layer (one for A, one for B and one for C), but rather for each edge between those layers (one between A and B and another between B and C). Here, unless your back-end is a Node.js application, we have to duplicate these data structure declarations because we are in the edge between two different programming languages.
出于不同的目的,在不同的层中使用不同的类是一个好习惯,但实际情况并非如此。 如果您具有三层(A-> B-> C),则不应为每一层都具有特定的数据结构s (对于A来说是一层,对于B来说是一层,对于C来说就是一层),而对于这些层之间的每条边 (一个在A和B之间,另一个在B和C之间)。 在这里,除非您的后端是Node.js应用程序,否则我们必须复制这些数据结构声明,因为我们处于两种不同编程语言之间的边缘。
To avoid writing everything twice, we are left with just one option…
为了避免重复编写所有内容,我们只剩下一个选择……
One day I was working on a .NET project with Entity Framework. It had a model diagram in a .edmx file, and if I changed this file, I had to select an option to generate the classes for POCO entities (Plain Old CLR Objects).
有一天,我正在使用Entity Framework进行.NET项目。 它在.edmx文件中有一个模型图,如果更改了此文件,则必须选择一个选项来生成POCO实体(普通的旧CLR对象)的类。
This code generation was done by T4, a template engine of Visual Studio that worked with a .tt file as a template for a C# class. It ran the code that reads the .edmx model file and outputs the classes in .cs files. After remembering that, I thought that it could be a solution to generate TypeScript interfaces and I started trying to make it work.
此代码生成是由T4(Visual Studio的模板引擎)完成的,T4使用.tt文件作为C#类的模板。 它运行了读取.edmx模型文件并在.cs文件中输出类的代码。 记住这一点之后,我认为这可能是生成TypeScript接口的解决方案,因此我开始尝试使其工作。
First, I tried to write my own template. When I worked with this and the Entity Framework, I never had to change the .tt template. Then I found out that Visual Studio didn’t support syntax highlighting in .tt files — it was like programming in notepad but worse.
首先,我尝试编写自己的模板。 当我使用此框架和Entity Framework时,我从未需要更改.tt模板。 然后我发现Visual Studio不支持.tt文件中的语法突出显示-就像在记事本中编程一样,但效果更糟。
Besides having C# code of the generation logic, I also had mixed with it the TypeScript code that had to be generated, like this. I installed a Visual Studio extension to get syntax support, but the extension defined syntax colors only for the light theme of Visual Studio, and I use the dark one. The light theme syntax colors on the dark theme was unreadable, so I had to change my Visual Studio theme too.
除了具有生成逻辑的C#代码,我还与它混合时必须产生的,像打字稿码本 。 我安装了Visual Studio扩展以获取语法支持,但是该扩展仅针对Visual Studio的浅色主题定义了语法颜色,而我使用了深色。 深色主题上的浅色主题语法颜色不可读,因此我也必须更改Visual Studio主题。
Now with syntax highlighting it was all good. It was time to start writing some code. I searched on google for a working example. My idea was to change it for my needs after I got it working, but… IT DIDN’T WORK!
现在突出显示语法,一切都很好。 是时候开始编写一些代码了。 我在Google上搜索了一个有效的示例。 我的想法是在我开始工作后根据自己的需要对其进行更改,但是……这没用!
System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=4.2.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.
I tried a lot of “working” examples found searching on google, but none of them worked. I thought that maybe the problem was not with Visual Studio or with the T4 Engine — maybe the problem was me, using it wrong.
我尝试了很多在Google上搜索到的“有效”示例,但没有一个有效。 我以为问题可能出在Visual Studio或T4 Engine上,不是我的问题,使用错了。
Then google got me on this issue in the .NET Core repository and I found that it didn’t work with ASP.NET Core projects. But this error was a common error in the .NET world, so I figured I could try to make a workaround for it. I searched for that 4.2.1.0 version of the System.Runtime.dll, I found it, and I tried to put it in some different directories to see if Visual Studio could find it…but nothing worked.
然后,谷歌让我在.NET Core存储库中遇到了这个问题 ,我发现它不适用于ASP.NET Core项目。 但是此错误是.NET世界中的常见错误,因此我认为我可以尝试为此解决方法。 我搜索了System.Runtime.dll的4.2.1.0版本,找到了它,然后尝试将其放在其他目录中以查看Visual Studio是否可以找到它……但没有任何效果。
Finally, I used Process Explorer to see which version of System.Runtime Visual Studio had loaded, and it was version 4.0.0.0. I tried to use a bindingRedirect
to force it to use the same version (as I described here), and it worked! I could not believe that I would not have to duplicate and manually sync my data structures between the server and client anymore.
最后,我使用Process Explorer查看了哪个版本的System.Runtime Visual Studio已加载,该版本为4.0.0.0。 我尝试使用bindingRedirect
强制它使用相同的版本(如我在此处所述),并且它起作用了! 我不敢相信我不再需要在服务器和客户端之间复制和手动同步数据结构。
I started to thing about it more, and another thought was bothering me…
我开始对此事做更多的事情,另一个想法困扰着我……
I work for a big oil company, with a lot of legacy applications. A friend had to work with a virtual machine because the app he was debugging sometimes only worked in Windows XP. Another app that I had to work on one day only worked with Visual Studio 2010. Another that used Code Contracts only worked with Visual Studio 2013 because the Code Contracts extension didn’t work in Visual Studio 2015 or 2017.
我在一家大型石油公司工作,有很多遗留应用程序。 一个朋友必须使用虚拟机,因为他调试的应用程序有时只能在Windows XP中运行。 我一天必须使用的另一个应用程序只能在Visual Studio 2010中使用。另一个使用代码合同的应用程序只能在Visual Studio 2013中使用,因为代码合同扩展在Visual Studio 2015或2017中不起作用。
Since 2012 when I started working there until the beginning of 2019, I never had the chance to develop a new application. All my work always was with others developers’ messes. Last year I started to study more about software architecture, and I read the Uncle Bob’s “Clean Architecture” book.
从2012年开始在那里工作到2019年初,我再也没有机会开发新的应用程序。 我所有的工作总是与其他开发人员的混乱。 去年,我开始研究有关软件体系结构的更多信息,并且阅读了Bob叔叔的“ Clean Architecture”一书。
Now that I started this new year with this opportunity, for the first time in this company I am creating a web application from scratch and I want to do a good job. I choose ASP.NET Core for my back-end, React for the front-end, and it will be one of the first apps in this company to run in a Docker container in our new Kubernetes cluster.
既然有了这个机会,我就开始了新的一年,这是我第一次在该公司从头开始创建一个Web应用程序,我想做得很好。 我为后端选择了ASP.NET Core,为前端选择了React,它将成为该公司中首批在新Kubernetes集群中的Docker容器中运行的应用程序之一。
Another poor developer will have to work on this project in the future, with my code and all my mess, and I don’t want them to have to deal with bad code. I want all developers after me to want to work on this project. This will not happen if they have to lose a day of work just to get the generation of client code from back-end data structures working. They would then hate me (and some of them would already hate me for putting TypeScript code in a project when TypeScript was still in version 0.9).
将来,另一位可怜的开发人员将不得不使用我的代码和所有混乱情况来从事此项目,并且我不希望他们必须处理不良代码。 我希望我之后的所有开发人员都想从事这个项目。 如果他们只需要花一天的时间才能从后端数据结构中生成客户端代码,就不会发生这种情况。 然后他们会讨厌我(其中有些人已经讨厌我在TypeScript仍为0.9版时将TypeScript代码放入项目中)。
When we write code that isn’t ours, we have the responsibility to make it easy for other people to work on it.
当我们编写非我们自己的代码时,我们有责任使其他人更轻松地进行处理。
After thought about that, I came to a conclusion:
经过考虑,我得出了一个结论:
We should avoid dependencies on anything that cannot be handled by the package manager of the technology of choice.
我们应该避免依赖于所选技术的程序包管理器无法处理的任何事情。
In this case, besides dependencies on Visual Studio and Windows, I would make the project depend on a bug fix that would need to be fixed by Microsoft (and it seems that it doesn’t have any priority). So it’s best to duplicate this code and manually sync it than put a dependency on this T4 engine.
在这种情况下,除了依赖Visual Studio和Windows之外,我还要使项目依赖于Microsoft需要修复的错误修复(而且似乎没有任何优先级 )。 因此,最好是复制此代码并手动同步它,而不要依赖于此T4引擎。
I choose to use .NET Core, but if some developer in the future wants to work on this project using Linux, I can’t stop them.
我选择使用.NET Core,但是如果将来有一些开发人员想要使用Linux从事该项目,我将无法阻止他们。
Duplicate code is bad, but dependency on third party tools is worse. So, what can we do to avoid duplication of data structures and not depend on any specific IDE / plugin / extension / tool for development?
重复的代码不好,但是对第三方工具的依赖性更差。 那么,我们该如何避免数据结构重复并且不依赖任何特定的IDE /插件/扩展/开发工具?
It took me some time to realize that the only tool that I needed was there all this time, inside the language runtime: Reflection.
我花了一些时间才意识到,我所需要的唯一工具一直都在语言运行时内部: Reflection 。
I realized I could write some code that runs on the startup of my back-end ASP.NET Core app only in development mode. This code could use reflection to read the metadata about names and types of all the data structures that I wanted to generate TypeScript interfaces. I just needed to map C# primitives to TypeScript primitives, write the .d.ts TypeScript definitions in a specific folder, and I’d be done.
我意识到我可以编写一些仅在开发模式下在后端ASP.NET Core应用程序启动时运行的代码。 这段代码可以使用反射来读取有关我想要生成TypeScript接口的所有数据结构的名称和类型的元数据。 我只需要将C#原语映射到TypeScript原语,在特定文件夹中编写.d.ts TypeScript定义,就可以完成。
Every time I changed some data structure in the back-end, it would override the interfaces definitions inside a .d.ts files when I ran the code to test it. When I got to the part of writing the client code to use the data structure that changed, the interfaces would already be updated.
每当我在后端更改某些数据结构时,当我运行代码进行测试时,它将覆盖.d.ts文件中的接口定义。 当我开始编写客户机代码以使用更改的数据结构时,接口将已经更新。
This approach can be used by projects in .NET, Java, Python, and any other language that has support for code reflection, without adding a dependency on any IDE / plugin / extension / tool.
.NET,Java,Python和任何其他支持代码反射的语言的项目都可以使用此方法,而无需添加对任何IDE /插件/扩展/工具的依赖。
I wrote a simple example using C# with ASP.NET Core and published it on GitHub here. It just takes from all classes that inherit Microsoft.AspNetCore.Mvc.ControllerBase
and all types from parameters and returns types of public methods that have HttpGet
or HttpPost
attributes.
我写了使用C#与ASP.NET核心一个简单的例子,并将其发布在GitHub上这里 。 它仅从继承Microsoft.AspNetCore.Mvc.ControllerBase
所有类以及参数的所有类型中获取,并返回具有HttpGet
或HttpPost
属性的公共方法的类型。
Here is what the generated interfaces look like:
生成的接口如下所示:
I used it to generate interfaces and enums for data structures only, but think about the code below:
我仅使用它生成数据结构的接口和枚举,但请考虑以下代码:
It’s much less of a pain to keep this code in sync with all the possible MVC controllers and actions than it was to keep the data structures in sync. But do I need to write this code by hand? Couldn’t it be generated too?
保持此代码与所有可能的MVC控制器和操作同步的痛苦要比保持数据结构的同步少得多。 但是我需要手工编写此代码吗? 也不能生成吗?
I can’t generate C# interfaces from C# concrete implementations, because I need the code to compile and run before I can use reflection to generate it. But with client code that needs to be kept in sync with server code, I can generate it. This way of code generation can be used beyond the data structure interfaces.
我无法从C#具体实现中生成C#接口,因为在使用反射来生成它之前,我需要代码进行编译和运行。 但是有了需要与服务器代码保持同步的客户端代码,我就可以生成它。 可以在数据结构接口之外使用这种代码生成方式。
It doesn’t need to be written with TypeScript. If you don’t like TypeScript and prefer to use plain Javascript, you can write your .js files and use TypeScript just as a tool (if you use Visual Studio Code you are already using it). That way, you can generate helper functions that convert your data structures to the same structures. It seems weird, but it would help the TypeScript Language Service to analyse your code and tell Visual Studio Code with fields that exist in each object, so it could help you to write your code.
它不需要用TypeScript编写。 如果您不喜欢TypeScript而喜欢使用纯Javascript,则可以编写.js文件并将TypeScript用作工具(如果您使用的是Visual Studio Code,则已经在使用它)。 这样,您可以生成将数据结构转换为相同结构的辅助函数。 看起来很奇怪,但是它可以帮助TypeScript语言服务分析您的代码,并使用每个对象中存在的字段告诉Visual Studio Code,从而可以帮助您编写代码。
We, as developers, have a responsibility to other developers that will have to work on our code. Don’t leave a mess for them to clean up, because they won’t (or at least they won’t want to!). They will likely only make it worse for the next one.
作为开发人员,我们对必须处理我们的代码的其他开发人员负责。 不要乱糟糟地清理它们,因为它们不会(或者至少它们不想!)。 他们可能只会使下一个变得更糟。
You should avoid at all costs any development and runtime dependencies that cannot be handled by the package manager. Don’t make your project the one that others developers will hate working on.
您应该不惜一切代价避免程序包管理器无法处理的任何开发和运行时依赖项。 不要让您的项目成为其他开发人员讨厌的项目。
Thanks for reading!
谢谢阅读!
PS 1: This repository with my code is just an example. The code that converts C# classes into TypeScript interfaces there is not good. You can do a lot better, and maybe we already have some NuGet package that do this.
PS 1:这个包含我的代码的存储库只是一个示例。 将C#类转换为TypeScript接口的代码是不好的。 您可以做得更好,也许我们已经有一些NuGet软件包可以做到这一点。
PS 2: I love TypeScript. If you love TypeScript too, you may want to take a look at these links, from before it was announced by Microsoft in 2012:
PS 2:我喜欢TypeScript。 如果您也喜欢TypeScript,则可能要看一下Microsoft在2012年宣布之前的以下链接:
What’s Microsoft’s father of C#’s next trick? Microsoft Technical Fellow Anders Hejlsberg is working on something to do with JavaScript tools. Here are a few clues about his latest project.
微软的C#下一个诀窍之父是什么? 微软技术研究员Anders Hejlsberg正在研究与JavaScript工具有关的东西。 以下是有关他最新项目的一些线索。
A HackerNews discussion: “Anders Hejlsberg Is Right: You Cannot Maintain Large Programs In JavaScript”
HackerNews讨论: “ Anders Hejlsberg是对的:您无法使用JavaScript维护大型程序”
A Channel9 video: “Anders Hejlsberg: Introducing TypeScript”
Channel9视频: “ Anders Hejlsberg:TypeScript简介”
翻译自: https://www.freecodecamp.org/news/the-easy-way-to-get-typescript-interfaces-from-c-java-or-python-code-in-any-ide-c3acac1e366a/
c# typescript