简体   繁体   English

如何在 TypeScript 项目中重用现有的 C# 类定义

[英]How to reuse existing C# class definitions in TypeScript projects

I am just going to start use TypeScript in my HTML client project which belongs to a MVC project with a entity framework domain model already there.我将开始在我的 HTML 客户端项目中使用TypeScript ,该项目属于一个已经存在实体框架域模型的 MVC 项目。 I want my two projects (client side and server side) totally separated as two teams will work on this... JSON and REST is used to communicate objects back and forth.我希望我的两个项目(客户端和服务器端)完全分开,因为两个团队将致力于此……JSON 和 REST 用于来回传递对象。

Of course, my domain objects on the client side should match the objects on the server side.当然,我在客户端的domain对象应该与服务器端的对象相匹配。 In the past, I have normally done this manually.过去,我通常手动完成此操作。 Is there a way to reuse my C# class definitions (specially of the POJO classes in my domain model) to create the corresponding classes in TypeScript"?有没有办法重用我的 C# 类定义(特别是我的域模型中的POJO类)来在 TypeScript 中创建相应的类”?

There is not currently anything that will map C# to TypeScript.目前没有任何东西可以将 C# 映射到 TypeScript。 If you have a lot of POCOs or you think they might change often, you could create a converter - something simple along the lines of...如果您有很多 POCO 或者您认为它们可能经常更改,您可以创建一个转换器 - 一些简单的......

public class MyPoco {
    public string Name { get; set; }
}

To

export class MyPoco {
    public Name: string;
}

There is also a discussion on Codeplex about auto-generating from C# .在 Codeplex 上也有关于从 C# 自动生成的讨论。

Just to keep things updated, TypeLite can generate TypeScript interfaces from C#:为了保持更新,TypeLite 可以从 C# 生成 TypeScript 接口:

http://type.litesolutions.net/ http://type.litesolutions.net/

Web Essentials allow to compile C# files to TypeScript .d.ts files on save. Web Essentials允许在保存时将 C# 文件编译为 TypeScript .d.ts文件。 Then you could reference the definitions from your .ts files.然后您可以从.ts文件中引用定义。

在此处输入图像描述

If you use vscode you can use my extension csharp2ts which does exactly that.如果你使用 vscode,你可以使用我的扩展csharp2ts ,它就是这样做的。

You just select the pasted C# code and run the Convert C# to TypeScript command from the command palette您只需选择粘贴的 C# 代码并从命令面板运行Convert C# to TypeScript命令在此处输入图像描述 A conversion example:转换示例:

public class Person
{
    /// <summary>
    /// Primary key
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Person name
    /// </summary>
    public string Name { get; set; }
}

to

export interface Person
{
    /**Primary key */
    Id : number;

    /**Person name */
    Name : string;
}

TypeLite and T4TSs above both looked good, just picked one, TypeLite, forked it to get support for上面的 TypeLite 和 T4TSs 看起来都不错,只是选择了一个,TypeLite,将其分叉以获得对

  • ValueTypes ,值类型
  • Nullables可为空
  • camelCasing (TypeScript root doc uses camels, and this goes too nice together with C#) camelCasing (TypeScript 根文档使用驼峰,这与 C# 一起使用太好了)
  • public fields (love clean and readable POCOs, also makes it easy for the C# Compiler)公共领域(喜欢干净和可读的 POCO,也使 C# 编译器更容易)
  • disable module generation禁用模块生成

Then I needed C# interfaces and thought it is time to bake my own thing and wrote a simple T4 script that just does what I need.然后我需要 C#接口并认为是时候烘焙我自己的东西并编写了一个简单的 T4 脚本来满足我的需要。 It also includes Enums .它还包括枚举 No repo required, just < 100 lines of T4.不需要回购,只需 < 100 行 T4。

Usage用法
No library, no NuGet, just this plain simple T4 file - use "add item" in Visual Studio and choose any T4 template.没有库,没有 NuGet,只有这个简单的 T4 文件——在 Visual Studio 中使用“添加项目”并选择任何 T4 模板。 Then paste this into the file.然后将其粘贴到文件中。 Adapt every line with "ACME" in it.用“ACME”调整每一行。 For every C# class add a line为每个 C# 类添加一行

<#= Interface<Acme.Duck>() #>

Order matters, any known type will be used in follwing interfaces.顺序很重要,任何已知类型都将用于以下接口。 If you use only interfaces, the file extension can be .d.ts , for enums you need a .ts file, since a variable is instantiated.如果你只使用接口,文件扩展名可以是.d.ts ,对于枚举你需要一个.ts文件,因为变量被实例化了。

Customisation客制化
Hack the script.破解脚本。

<#@ template debug="true" hostSpecific="true" language="C#" #>
<#@ output extension=".ts" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ assembly name="$(TargetDir)ACME.Core.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Linq" #>

<#= Interface<Acme.Bunny>() #>
<#= Interface<Acme.Duck>() #>
<#= Interface<Acme.Birdy>() #>
<#= Enums<Acme.CarrotGrade>() #>
<#= Interface<Acme.LinkParticle>() #>

<#+  
    List<Type> knownTypes = new List<Type>();

    string Interface<T>()
    {   
        Type t = typeof(T);     
        var sb = new StringBuilder();
        sb.AppendFormat("interface {0} {{\n", t.Name);
        foreach (var mi in GetInterfaceMembers(t))
        {
            sb.AppendFormat("  {0}: {1};\n", this.ToCamelCase(mi.Name), GetTypeName(mi));
        }
        sb.AppendLine("}");
        knownTypes.Add(t);
        return sb.ToString();
    }

    IEnumerable<MemberInfo> GetInterfaceMembers(Type type)
    {
        return type.GetMembers(BindingFlags.Public | BindingFlags.Instance)
            .Where(mi => mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property);
    }

    string ToCamelCase(string s)
    {
        if (string.IsNullOrEmpty(s)) return s;
        if (s.Length < 2) return s.ToLowerInvariant();
        return char.ToLowerInvariant(s[0]) + s.Substring(1);
    }

    string GetTypeName(MemberInfo mi)
    {
        Type t = (mi is PropertyInfo) ? ((PropertyInfo)mi).PropertyType : ((FieldInfo)mi).FieldType;
        return this.GetTypeName(t);
    }

    string GetTypeName(Type t)
    {
        if(t.IsPrimitive)
        {
            if (t == typeof(bool)) return "bool";
            if (t == typeof(char)) return "string";
            return "number";
        }
        if (t == typeof(decimal)) return "number";            
        if (t == typeof(string)) return "string";
        if (t.IsArray)
        {            
            var at = t.GetElementType();
            return this.GetTypeName(at) + "[]";
        }
        if(typeof (System.Collections.IEnumerable).IsAssignableFrom(t)) 
        {
            var collectionType = t.GetGenericArguments()[0]; // all my enumerables are typed, so there is a generic argument
            return GetTypeName(collectionType) + "[]";
        }            
        if (Nullable.GetUnderlyingType(t) != null)
        {
            return this.GetTypeName(Nullable.GetUnderlyingType(t));
        }
        if(t.IsEnum) return "number";
        if(knownTypes.Contains(t)) return t.Name;
        return "any";
    }

    string Enums<T>() // Enums<>, since Enum<> is not allowed.
    {
        Type t = typeof(T);        
        var sb = new StringBuilder();        
        int[] values = (int[])Enum.GetValues(t);
        sb.AppendLine("var " + t.Name + " = {");
        foreach(var val in values) 
        {
            var name = Enum.GetName(typeof(T), val);
            sb.AppendFormat("{0}: {1},\n", name, val);
        }
        sb.AppendLine("}");
        return sb.ToString();
    }
#>

The next level of the script will be to create the service interface from the MVC JsonController class.脚本的下一级将从 MVC JsonController 类创建服务接口。

Try Reinforced.Typings framework.试试 Reinforced.Typings 框架。 Seems that it solves your problem.似乎它解决了你的问题。

  1. Install it from NuGetNuGet安装
  2. Navigate to your POCO and add [TsInterface] attribute above it导航到您的 POCO 并在其上方添加[TsInterface]属性

    using Reinforced.Typings.Attributes; namespace YourNamespace { [TsInterface] public class YourPoco { public int YourNumber { get;set; } public string YourString { get;set; } public List<string> YourArray { get;set; } public Dictionary<int, object> YourDictionary { get;set; } } }
  3. Rebuild your project重建你的项目
  4. Find out generated TypeScript code in %Your_Project_Directory%/Scripts/project.ts file and add it to project manually%Your_Project_Directory%/Scripts/project.ts文件中找出生成的 TypeScript 代码,并手动将其添加到项目中

    module YourNamespace { export interface IYourPoco { YourNumber: number; YourString: string; YourArray: string[]; YourDictionary: { [key: int]: any }; } }
  5. Do the same for all your POCOs and reference project.ts in your other TypeScript code.对所有 POCO 执行相同操作,并在其他 TypeScript 代码中引用project.ts

See more details in documentation wiki文档 wiki中查看更多详细信息

Here's my approach to solving it.这是我解决它的方法。 Declare your C# classes with an attribute and.d.ts-files will be generated (using T4 transforms).使用属性声明您的 C# 类,将生成 .d.ts 文件(使用 T4 转换)。 There's a package on nuget and the source is available on github . nuget 上有一个包,源代码在 github 上可用 I'm still working on the project, but the support is pretty extensive.我仍在从事该项目,但支持非常广泛。

If you're using Visual Studio, add the Typewriter extension.如果您使用的是 Visual Studio,请添加 Typewriter 扩展。

Visual Studio Gallery 视觉工作室画廊

Website/Documentation网站/文件

Update更新

With Web Essentials installed in VS 2015, you can right-click the class file, then > Web Essentials > Create Typescript Intellisense File from the context menu.在 VS 2015 中安装Web Essentials后,您可以右键单击类文件,然后从上下文菜单中选择 > Web Essentials > 创建 Typescript Intellisense 文件。

I have created a small utility that can generate TypeScript interfaces from C# classes.我创建了一个小实用程序,可以从 C# 类生成 TypeScript 接口。 Is available as a NuGet package .作为NuGet 包提供。 Detailed documentation can be found on the project webpage .可以在项目网页上找到详细的文档。

Please have a look at this library Typewriter请看看这个图书馆打字机

It converts not only classes, enums, interfaces etc, but also the api-controllers, which is simply awesome..它不仅可以转换类、枚举、接口等,还可以转换 api 控制器,这简直太棒了……

Plus it does as this as soon as you save the source.cs file, so I don't have to trigger any external tool.此外,它会在您保存 source.cs 文件后立即执行此操作,因此我不必触发任何外部工具。 Save.cs and you get updated.ts Save.cs 你会得到 updated.ts

I have a little solution which uses T4 templates ( view source ).我有一个使用 T4 模板的小解决方案( 查看源代码)。

You go from any CLR POCO:您来自任何 CLR POCO:

public class Parent : Person
{
    public string Name { get; set; }
    public bool? IsGoodParent { get; set; }
    public virtual ICollection<Child> Children { get; set; }
}

To a TypeScript interface:到 TypeScript 接口:

///<reference path="Child.d.ts" />
///<reference path="Person.d.ts" />
interface Parent extends Person {
    Name : string;
    IsGoodParent? : bool;
    Children : Child[];
}

If you need to create a separate file for every generated TypeScript class/interface (ie in a "single class per file" manner), you can try TypeGen .如果您需要为每个生成的 TypeScript 类/接口创建一个单独的文件(即以“每个文件一个类”的方式),您可以尝试TypeGen It's a tool you can use from the Package Manager Console to generate TypeScript files based on your C# classes/enums.您可以从包管理器控制台使用它来根据您的 C# 类/枚举生成 TypeScript 文件。 It currently supports:目前支持:

  • exporting enums;导出枚举; exporting POCOs as TS classes or interfaces将 POCO 导出为 TS 类或接口
  • inheritance遗产
  • generic types通用类型
  • collection / nested collection types集合/嵌套集合类型

plus some additional features.加上一些额外的功能。 It's also open source (you can check it out on github ).它也是开源的(您可以在github上查看)。

You can use the open-source project NSwag : In the GUI, you can select .NET class from an existing .NET DLL and generate the TypeScript interface for it.您可以使用开源项目NSwag :在 GUI 中,您可以从现有的 .NET DLL 中选择 .NET 类并为其生成 TypeScript 接口。

The project also provides command line tools and support for T4 templates as well as generation of client code for Web API controllers...该项目还提供命令行工具和对 T4 模板的支持,以及为 Web API 控制器生成客户端代码......

Guys have a look at https://github.com/reinforced/ Reinforced.Typings .伙计们看看https://github.com/reinforced/Reinforced.Typings I have been playing with typelite and t4 templates for last few days and ended up with this project.最近几天我一直在玩 typelite 和 t4 模板,并以这个项目结束。 It is super-simple and works like a charm.它超级简单,工作起来很有魅力。 Just get the package, modify the configuration file (it is like for 10 seconds) and build.只需获取软件包,修改配置文件(大约 10 秒)并构建。 All gets done automatically without any issues.一切都自动完成,没有任何问题。 Bless the author!祝福作者!

The bad thing about T4 templates is that once you build from VS the scanned assemblies are locked and you must restart VS (how silly is that?). T4 模板的坏处在于,一旦您从 VS 构建,扫描的程序集就会被锁定,您必须重新启动 VS(这有多愚蠢?)。 There are some workaround in T4 Toolbox + some VS cleaning directives but none of these worked for me. T4 工具箱中有一些解决方法 + 一些 VS 清理指令,但这些都不适合我。

You can also use this: https://github.com/pankleks/TypeScriptBuilder你也可以使用这个: https ://github.com/pankleks/TypeScriptBuilder

This small library generates TypeScript type definition based on C# types.这个小型库基于 C# 类型生成 TypeScript 类型定义。 Use it directly in your backend C# project to generate code for your frontend TypeScript project.直接在后端 C# 项目中使用它来为前端 TypeScript 项目生成代码。 You can also wrtie small console app, to generate code by pre-build tools.您还可以编写小型控制台应用程序,通过预构建工具生成代码。

Works on Full & NET Core framework!适用于 Full & NET Core 框架!

Install by nuget: Install-Package TypeScriptBuilder通过 nuget 安装: Install-Package TypeScriptBuilder

Supported features支持的功能

  • Resolving type dependency解决类型依赖
  • Generics泛型
  • Type inheritance类型继承
  • Namespaces (modules)命名空间(模块)
  • Enums枚举
  • Nullable types可空类型
  • Dictionary converison (to strong type TS indexed objects)字典转换(强类型 TS 索引对象)
  • Set of code generation control attributes一组代码生成控制属性
  • any for types that can't be converted any对于无法转换的类型

More description: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md更多说明: https ://github.com/pankleks/TypeScriptBuilder/blob/master/README.md

I wrote a feature request about this on the Developer Community page:我在开发者社区页面上写了一个关于这个的功能请求:

https://developercommunity.visualstudio.com/idea/1153873/reuse-existing-net-classes-for-typescript-definiti.html https://developercommunity.visualstudio.com/idea/1153873/reuse-existing-net-classes-for-typescript-definiti.html

As you can see in the answers here there are many projects that tries to solve this but unfortunately many of these projects are single developer projects that stops being supported along the way.正如您在此处的答案中看到的那样,有许多项目试图解决此问题,但不幸的是,其中许多项目都是单一开发人员项目,在此过程中不再受到支持。 I think it would be really neat if Microsoft maintained one of these projects instead, creating a .NET to TypeScript generator.我认为,如果微软改为维护这些项目之一,创建一个.NETTypeScript生成器,那将是非常好的。

Among others, some are still maintained but relies heavily on a single developer:其中,有些仍在维护但严重依赖单个开发人员:

TypeLite:类型精简版:

https://bitbucket.org/LukasKabrt/typelite/src/default/ https://bitbucket.org/LukasKabrt/typelite/src/default/

TypeScript Definition Generator: TypeScript 定义生成器:

https://marketplace.visualstudio.com/items?itemName=MadsKristensen.TypeScriptDefinitionGenerator https://marketplace.visualstudio.com/items?itemName=MadsKristensen.TypeScriptDefinitionGenerator

Typewriter:打字机:

https://github.com/frhagn/Typewriter https://github.com/frhagn/Typewriter

TypeGen:类型代:

https://github.com/jburzynski/TypeGen https://github.com/jburzynski/TypeGen

Reinforced.Typings:强化。打字:

https://github.com/reinforced/Reinforced.Typings https://github.com/reinforced/Reinforced.Typings

My tip would be to choose a project that you from the start expect to stop being maintained and is prepared to switch to something that is currently maintained.我的建议是选择一个您从一开始就希望停止维护并准备切换到当前维护的项目的项目。 I would go for something that uses annotations and cross my fingers I could replace simply that tag with something else if that project stops being supported.我会选择使用注释的东西,如果该项目不再受支持,我可以用其他东西简单地替换那个标签。

How about the other way around?反过来呢?

Check out erecruit TypeScript Translator .查看erecruit TypeScript Translator It comes with ready to go C# support, but is actually template-based (uses Nunjucks for rendering), which means it can generate anything else - VB.NET, F#, C++, XML, SQL - whatever you can encode with a template.它带有随时可用的 C# 支持,但实际上是基于模板的(使用 Nunjucks 进行渲染),这意味着它可以生成任何其他东西 - VB.NET、F#、C++、XML、SQL - 任何你可以用模板编码的东西。

Works as a .NET console program, NodeJS program (for those not on Windows), or as a Visual Studio extension , complete with generate-on-save functionality.作为 .NET 控制台程序、NodeJS 程序(对于那些不在 Windows 上的程序)或作为Visual Studio 扩展,完成保存时生成功能。 And includes MSBuild support, just to make your build server happy.并包括 MSBuild 支持,只是为了让您的构建服务器满意。 :-) :-)

I like the citykid solution.我喜欢 citykid 解决方案。 I had extend it a little bit.我把它延长了一点。 So, solution also is based on codegeneration technique with T4 templates.因此,解决方案也基于使用 T4 模板的代码生成技术。

It can generate common TypeScript types and ambient declarations.它可以生成常见的 TypeScript 类型和环境声明。

It supports inheritance and interface implementations.它支持继承和接口实现。

Supports generics, arrays and lists as type fields.支持泛型、数组和列表作为类型字段。

Also it translates to TypeScript types that doesn't explicitly mentioned in configuration (for example we import type A, and in TS output you can find some other types: types of the fields, base types and interfaces).它还转换为未在配置中明确提及的 TypeScript 类型(例如,我们导入类型 A,并且在 TS 输出中您可以找到一些其他类型:字段类型、基本类型和接口)。

You can also override type's name.您还可以覆盖类型的名称。

Enums are also supported.也支持枚举。

Usage example (you can find it in the project repository):使用示例(您可以在项目存储库中找到它):

// set extension of the generated TS file
<#@ output extension=".d.ts" #>

// choose the type of TS import TsMode.Ambient || TsMode.Class
<# var tsBuilder = new TsBuilder(TsMode.Ambient); #>

// reference assembly with the c# types to be transformed
<#@ assembly name="$(SolutionDir)artifacts\...\CsT4Ts.Tests.dll" #>

// reference namespaces
<#@ import namespace="CsT4Ts.Tests" #>
<#
    //add types to processing
    tsBuilder.ConsiderType(typeof(PresetDTOBase), "PresetBase");
    tsBuilder.ConsiderType(typeof(PresetDTO), "Preset");
    tsBuilder.ConsiderType(typeof(TestInterface<,>));
#>

// include file with transformation algorithms
<#@ include file="CsT4Ts.t4" #>

And you will get an output你会得到一个输出

//CsT4Ts.Tests.PresetDTOBase => PresetBase
// CsT4Ts.Tests.PresetDTO => Preset
// CsT4Ts.Tests.TestInterface`2 => TestInterface
// CsT4Ts.Tests.TestEnum => TestEnum

declare class PresetBase
{
    PresetId: string;
    Title: string;
    InterviewDate: string;
}

declare class Preset extends PresetBase
{
    QuestionsIds: string[];
}

declare interface TestInterface<TA, TB>
{
    A: string;
    B: number;
    C: TestEnum;
    D: TestEnum[];
    E: number[];
    F: TA;
    G: TB[];
}

declare enum TestEnum
{
    Foo = 10,
    Boo = 100
}

Check full solution here: https://bitbucket.org/chandrush/cst4ts在此处查看完整解决方案: https ://bitbucket.org/chandrush/cst4ts

You can also use Bridge.net.您也可以使用 Bridge.net。 From version 1.7 it supports generation of TypeScript definitions for C# types.从 1.7 版开始,它支持为 C# 类型生成 TypeScript 定义。 See http://bridge.net/docs/generate-typescript-definitions/请参阅http://bridge.net/docs/generate-typescript-definitions/

I also liked @citykid's answer a lot, so I extended it to do a whole namespace at a time.我也非常喜欢@citykid 的回答,所以我将其扩展为一次处理整个命名空间。 Just put the POCO classes into the namespace, and rebuild T4 templates.只需将 POCO 类放入命名空间,然后重建 T4 模板。 I wish I know how to generate separate files for each, but that not the end of the world.我希望我知道如何为每个文件生成单独的文件,但这不是世界末日。

You need to reference the.DLL files in the top part (where the classes are that you want), and you need to mention the namespaces.您需要引用顶部的 .DLL 文件(您想要的类所在的位置),并且需要提及命名空间。 All lines to edit are marked with ACME.所有要编辑的行都标有 ACME。 Major cudos to @citykid, appreciate it! @citykid 的主要荣誉,感激不尽!

<#@ template debug="true" hostSpecific="true" language="C#" #>
<#@ output extension=".ts" #>
<#@ Assembly Name="System.Core.dll" #>
<#@ assembly name="$(TargetDir)YOUR_DLL_NAME_HERE_ACME.dll" #>
<#@ assembly name="$(TargetDir)YOUR_OTHER_DLL_NAME_HERE_ACME.dll" #>
<#@ assembly name="$(TargetDir)YOUR_OTHER_DLL_NAME_HERE_ACME.dll" #>
<#@ assembly name="$(TargetDir)YOUR_OTHER_DLL_NAME_HERE_ACME.dll" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Reflection" #>

<#= Process("My.Very.Special.Namespace.ACME") #>
<#= Process("My.Other.Very.Special.Namespace.ACME") #>
<#= Process("My.Other.Very.Special.Namespace.ACME") #>
<#= Process("My.Other.Very.Special.Namespace.ACME") #>

<#+  

    List<Type> knownTypes = new List<Type>();

    string Process(string nameSpace) {
      var allass = AppDomain.CurrentDomain.GetAssemblies();
      var ss = "";
      foreach (var ass in allass)
      {
         ss += ProcessAssembly(ass, nameSpace);
      }
      return ss;
    }

   string ProcessAssembly(Assembly asm, string nameSpace) {
      try {
            Type[] types;
            try
            {
                types = asm.GetTypes();
            }
            catch (ReflectionTypeLoadException e)
            {
                types = e.Types;
            }
            var s = "";
            foreach (var t in types.Where(t => t != null))
            {
               try {

               if (String.Equals(t.Namespace, nameSpace, StringComparison.Ordinal))
               {
                    s += InterfaceOfType(t);
               }

               } catch (Exception e)
               {
               }
            }
            return s;      
      }
      catch (Exception ee2) {
        return "// ERROR LOADING TYPES: " + ee2;
      }

   }

    string InterfaceOfType(Type T)
    {   
        Type t = T;     
        var sb = new StringBuilder();
        sb.AppendFormat("interface {0} {{\r\n", t.Name);
        foreach (var mi in GetInterfaceMembers(t))
        {
            sb.AppendFormat("  {0}: {1};\r\n", this.ToCamelCase(mi.Name), GetTypeName(mi));
        }
        sb.AppendLine("}");
        knownTypes.Add(t);
        return sb.ToString();
    }

    string Interface<T>()
    {   
        Type t = typeof(T);     
        var sb = new StringBuilder();
        sb.AppendFormat("interface {0} {{\n", t.Name);
        foreach (var mi in GetInterfaceMembers(t))
        {
            sb.AppendFormat("  {0}: {1};\r\n", this.ToCamelCase(mi.Name), GetTypeName(mi));
        }
        sb.AppendLine("}");
        knownTypes.Add(t);
        return sb.ToString();
    }

    IEnumerable<MemberInfo> GetInterfaceMembers(Type type)
    {
        return type.GetMembers(BindingFlags.Public | BindingFlags.Instance)
            .Where(mi => mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property);
    }

    string ToCamelCase(string s)
    {
        if (string.IsNullOrEmpty(s)) return s;
        if (s.Length < 2) return s.ToLowerInvariant();
        return char.ToLowerInvariant(s[0]) + s.Substring(1);
    }

    string GetTypeName(MemberInfo mi)
    {
        Type t = (mi is PropertyInfo) ? ((PropertyInfo)mi).PropertyType : ((FieldInfo)mi).FieldType;
        return this.GetTypeName(t);
    }

    string GetTypeName(Type t)
    {
        if(t.IsPrimitive)
        {
            if (t == typeof(bool)) return "boolean";
            if (t == typeof(char)) return "string";
            return "number";
        }
        if (t == typeof(decimal)) return "number";            
        if (t == typeof(string)) return "string";
        if (t.IsArray)
        {            
            var at = t.GetElementType();
            return this.GetTypeName(at) + "[]";
        }
        if(typeof (System.Collections.IEnumerable).IsAssignableFrom(t)) 
        {
            var collectionType = t.GetGenericArguments()[0]; // all my enumerables are typed, so there is a generic argument
            return GetTypeName(collectionType) + "[]";
        }            
        if (Nullable.GetUnderlyingType(t) != null)
        {
            return this.GetTypeName(Nullable.GetUnderlyingType(t));
        }
        if(t.IsEnum) return "number";
        if(knownTypes.Contains(t)) return t.Name;
        return "any";
    }

    string Enums<T>() // Enums<>, since Enum<> is not allowed.
    {
        Type t = typeof(T);        
        var sb = new StringBuilder();        
        int[] values = (int[])Enum.GetValues(t);
        sb.AppendLine("var " + t.Name + " = {");
        foreach(var val in values) 
        {
            var name = Enum.GetName(typeof(T), val);
            sb.AppendFormat("{0}: {1},\r\n", name, val);
        }
        sb.AppendLine("}");
        return sb.ToString();
    }
#>

My solution was to write a small codegen util that simply takes a project assembly (and refering assemblies) and start scanning types that are involved in the interaction between typescript and c#.我的解决方案是编写一个小型 codegen 实用程序,它只需要一个项目程序集(和引用程序集)并开始扫描涉及 typescript 和 c# 之间交互的类型。 This util outputs both javascript as d.ts... The tool is called in the post-build event... works like a charm!此实用程序将两个 javascript 都输出为 d.ts...该工具在构建后事件中被调用...就像一个魅力!

If interested, you can use TypedRpc .如果有兴趣,您可以使用TypedRpc Its purpose is not only to create the interfaces in TypeScript, but to create all communication with the service in.Net using the JsonRpc protocol.它的目的不仅是在 TypeScript 中创建接口,而且还使用 JsonRpc 协议在 .Net 中创建与服务的所有通信。

Example for a class in server:服务器中的类示例:

[TypedRpc.TypedRpcHandler]
public class RpcServerExample
{
    public String HelloWorld()
    {
        return "Hello World!";
    }
}

Usage of generated TypeScript code:生成的 TypeScript 代码的用法:

/// <reference path="Scripts/TypedRpc.ts" />

let rpc: TypedRpc.RpcServerExample = new TypedRpc.RpcServerExample();

var callback = function(data, jsonResponse) {
    console.log(data);
};

rpc.HelloWorld().done(callback).fail(callback);

Check out https://github.com/Rodris/TypedRpc for other examples of how to use it.查看https://github.com/Rodris/TypedRpc了解如何使用它的其他示例。

ASP.NET Web API Client Generators may be more handy, less overhead than swagger toolchain and others during SDLC.在 SDLC 期间, ASP.NET Web API 客户端生成器可能比 swagger 工具链和其他工具更方便、开销更少。

While programmers generally use WebApiClientGen to generate client API codes, this project also provides POCO2TS.exe, a command line program that generates TypsScript interfaces from POCO classes.虽然程序员通常使用 WebApiClientGen 生成客户端 API 代码,但该项目还提供了 POCO2TS.exe,这是一个从 POCO 类生成 TypsScript 接口的命令行程序。 You may use either Poco2ts.exe or the poco2ts component to integrate the code generation with your build pipeline.您可以使用 Poco2ts.exe 或 poco2ts 组件将代码生成与构建管道集成。

If you want to convert it through node.js then you can use this package(csharp-to-typescript).如果你想通过 node.js 转换它,那么你可以使用这个包(csharp-to-typescript)。 https://www.npmjs.com/package/csharp-to-typescript https://www.npmjs.com/package/csharp-to-typescript

sourcode: https://github.com/YuvrajSagarRana/csharp-to-typescript源代码: https ://github.com/YuvrajSagarRana/csharp-to-typescript

eg: 
// After installation, import the package.
var { CsharpToTs, getConfiguration } = require("csharp-to-typescript");

// Use CsharpToTs to convert your source code a. Method one give your source code as string:
const sourceCodeInString =   `public class Address
{
  public int Id {get; set;}
  public string Street { get; set; }
  public string City { get; set; }
}`

var outputTypescript = CsharpToTs(sourceCodeInString, getConfiguration());
console.log(outputTypescript);

// Output is

export class Address
{
  Id: number;
  Street: string;
  City: string;
}

If you are using VSCode, you could take a look at that extension C# to TypeScript如果您使用的是 VSCode,则可以查看C# to TypeScript的扩展

将 C# 类转换为 TypeScript 接口

See how I can convert it from C# code in just a click.了解如何通过单击将其从 C# 代码转换。 Of course, not all the classes will be converted such as the factory, but it is good enough for the client-side.当然,并不是所有的类都会被转换,比如工厂,但是对于客户端来说已经足够好了。 We just need the shape of data.我们只需要数据的形状。 Sometimes If I need to use the visitor pattern, I will decorate with additional methods that I need.有时如果我需要使用访问者模式,我会用我需要的额外方法来装饰。 It took only like 5 seconds to do so.这样做只花了大约 5 秒钟。 Comparing with one minute above, I could say it is a big win.与上面的一分钟相比,我可以说这是一个巨大的胜利。

See more detail in my blog post在我的博客文章中查看更多详细信息

I use a small custom DSL that generates both TypeScript and C# DTO/POCO classes.我使用生成 TypeScript 和 C# DTO/POCO 类的小型自定义 DSL。 The DSL looks like the following: DSL 如下所示:

output CSharp
output TypeScript

namespace MyLibrary.Logic.DataModel.DTOs

class Something

property int ID
property string Name
property DateTime DateTime
property int ForeignKeyID

This way I have a DTO model that has a single source (the DSL) and sits between the two C# and TypeScript logic models.通过这种方式,我有一个 DTO 模型,它具有单一来源(DSL)并且位于两个 C# 和 TypeScript 逻辑模型之间。

I then write both a C# and TypeScript DTOFactory class that has methods responsible for converting between DTOs and Logic Model objects.然后,我编写了一个 C# 和 TypeScript DTOFactory 类,这些类具有负责在 DTO 和逻辑模型对象之间进行转换的方法。 (And part of the DTO generation are serialization methods that convert to an anonymous JSON-suitable type for serialization or for storing in IndexedDB.) (DTO 生成的一部分是序列化方法,这些方法转换为适合序列化或存储在 IndexedDB 中的匿名 JSON 类型。)

By doing this, as soon as there is a model change on either side, I get compilation errors until both sides match again.通过这样做,一旦任何一方的模型发生变化,我就会得到编译错误,直到双方再次匹配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 打字稿定义:如何在类中声明类 - Typescript definitions: how to declare class inside class TypeScript 类型定义:如何声明 class 构造函数 - TypeScript type definitions: how to declare class constructors 如何在运行时动态创建C#类(根据现有类) - How to create a C# class (according to an existing class) dynamically at runtime 如何连接两个项目? 一个用于打字稿,另一个用 bot 框架 c# 编写 - How to connect two projects? One for typescript and another is written in bot framework c# 如何在许多Visual Studio(2013)项目中处理TypeScript类型定义? - How do I handle TypeScript type definitions across many Visual Studio (2013) projects? 如何从现有的 object 初始化 typescript class? - How to initialize a typescript class from existing object? TypeScript 中 c# 类虚拟成员的等价物 - Equivalent of c# class virtual member in TypeScript 如何将 aws-cdk typescript 堆栈 class 构造函数 function 中的定义提取到单独的文件中? - How to extract definitions inside aws-cdk typescript stack class constructor function into separate files? 如何在JavaScript中重用C#代码进行数据验证? - How to reuse c# code for data validation in javascript? 我可以使用T4模板功能从javascript类定义生成C#类吗? - Can I use T4 template features to generate C# classes from javascript class definitions?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM