我将在我的HTML客户端项目中开始使用TypeScript
,该项目属于一个已经存在实体框架域模型的MVC项目。我希望我的两个项目(客户端和服务器端)完全分离,因为两个团队将在此工作...... JSON和REST用于来回传递对象。
当然,客户端的domain
个对象应与服务器端的对象匹配。在过去,我通常手动完成此操作。有没有办法重用我的C#类定义(特别是我的域模型中的POJO
类)来在TypeScript中创建相应的类“?
答案 0 :(得分:50)
目前没有任何内容可以将C#映射到TypeScript。如果您有很多POCO或者您认为它们可能会经常更改,您可以创建一个转换器 - 这很简单......
public class MyPoco {
public string Name { get; set; }
}
要
export class MyPoco {
public Name: string;
}
还有discussion on Codeplex about auto-generating from C#。
只是为了保持更新,TypeLite可以从C#生成TypeScript接口:
答案 1 :(得分:32)
Web Essentials允许在保存时将C#文件编译为TypeScript .d.ts
文件。然后,您可以引用.ts
文件中的定义。
答案 2 :(得分:22)
上面的TypeLite和T4TS看起来都很好,只选了一个,TypeLite,分叉它以获得支持
然后我需要C#接口,并认为是时候烘焙我自己的东西并写了一个简单的T4脚本,它只是做我需要的。它还包括枚举。不需要回购,只需< 100行T4。
<强>用法强>
没有库,没有NuGet,只是这个简单的简单T4文件 - 在Visual Studio中使用“添加项目”并选择任何T4模板。然后将其粘贴到文件中。使用“ACME”调整每一行。对于每个C#类添加一行
<#= Interface<Acme.Duck>() #>
订单很重要,任何已知类型都将用于以下接口。如果仅使用接口,则文件扩展名可以是 .d.ts ,对于需要 .ts 文件的枚举,因为变量已实例化。
<强>定制强>
破解脚本。
<#@ 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();
}
#>
该脚本的下一级将是从MVC JsonController类创建服务接口。
答案 3 :(得分:18)
这是我解决问题的方法。使用属性声明您的C#类,并生成.d.ts文件(使用T4转换)。有一个package on nuget,来源是available on github。我还在研究这个项目,但支持非常广泛。
答案 4 :(得分:17)
如果您使用的是Visual Studio,请添加Typewriter扩展名。
<强>更新强>
在VS 2015中安装Web Essentials后,您可以右键单击类文件,然后按&gt; Web Essentials&gt;从上下文菜单中创建Typescript Intellisense文件。
答案 5 :(得分:13)
如果您使用vscode,则可以使用我的扩展程序csharp2ts。
您只需选择粘贴的C#代码并从命令面板中运行Convert C# to TypeScript
命令
转换示例:
public class Person
{
/// <summary>
/// Primary key
/// </summary>
public int Id { get; set; }
/// <summary>
/// Person name
/// </summary>
public string Name { get; set; }
}
到
export interface Person
{
/**Primary key */
Id : number;
/**Person name */
Name : string;
}
答案 6 :(得分:11)
尝试使用Reinforced.Typings框架。似乎它解决了你的问题。
导航至您的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; }
}
}
在%Your_Project_Directory%/Scripts/project.ts
文件中找出生成的TypeScript代码,并手动将其添加到项目中
module YourNamespace {
export interface IYourPoco
{
YourNumber: number;
YourString: string;
YourArray: string[];
YourDictionary: { [key: int]: any };
}
}
project.ts
。答案 7 :(得分:10)
我创建了一个可以从C#类生成TypeScript接口的小实用程序。以NuGet package格式提供。详细文档可在project webpage上找到。
答案 8 :(得分:6)
请查看此库Typewriter
它不仅可以转换类,枚举,接口等,还可以转换api控制器,这简直太棒了..
另外,只要您保存源.cs文件,它就会这样做,因此我不必触发任何外部工具。保存.cs,你得到更新.ts
答案 9 :(得分:5)
我有一个使用T4模板的小解决方案(view source)。
你来自任何CLR POCO:
public class Parent : Person
{
public string Name { get; set; }
public bool? IsGoodParent { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
到TypeScript界面:
///<reference path="Child.d.ts" />
///<reference path="Person.d.ts" />
interface Parent extends Person {
Name : string;
IsGoodParent? : bool;
Children : Child[];
}
答案 10 :(得分:3)
您可以使用开源项目NSwag:在GUI中,您可以从现有的.NET DLL中选择.NET类,并为其生成TypeScript接口。
该项目还提供命令行工具和对T4模板的支持,以及为Web API控制器生成客户端代码......
答案 11 :(得分:2)
反过来怎么样?
结帐erecruit TypeScript Translator。它随时可用于C#支持,但实际上是基于模板的(使用Nunjucks进行渲染),这意味着它可以生成任何其他内容 - VB.NET,F#,C ++,XML,SQL - 无论您使用模板进行编码。
用作.NET控制台程序,NodeJS程序(适用于不在Windows上的程序)或Visual Studio extension,具有生成保存功能。并包括MSBuild支持,只是为了让您的构建服务器满意。 : - )
答案 12 :(得分:2)
您也可以使用此:https://github.com/pankleks/TypeScriptBuilder
这个小型库基于C#类型生成TypeScript类型定义。 直接在后端C#项目中使用它来为您的前端TypeScript项目生成代码。 您还可以使用小型控制台应用程序,通过预构建工具生成代码。
适用于Full&amp; NET Core框架!
由nuget安装:
Install-Package TypeScriptBuilder
any
表示无法转换的类型更多说明:https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md
答案 13 :(得分:1)
我喜欢citykid解决方案。我把它扩展了一点。 因此,解决方案也基于使用T4模板的代码生成技术。
它可以生成常见的TypeScript类型和环境声明。
它支持继承和接口实现。
支持泛型,数组和列表作为类型字段。
此外,它转换为配置中未明确提及的TypeScript类型(例如,我们导入类型A,在TS输出中,您可以找到其他类型:字段类型,基本类型和接口)。 / p>
您也可以覆盖类型名称。
也支持枚举。
用法示例(您可以在项目存储库中找到它):
// 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" #>
你会得到一个输出
//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
}
在此处检查完整解决方案:https://bitbucket.org/chandrush/cst4ts
答案 14 :(得分:1)
我也非常喜欢@ citykid的答案,所以我将它扩展为一次完成一个完整的命名空间。只需将POCO类放入命名空间,然后重建T4模板即可。我希望我知道如何为每个文件生成单独的文件,但这不是世界末日。
您需要在顶部引用.DLL文件(类所在的位置),您需要提及名称空间。所有要编辑的行都标有ACME。 @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();
}
#>
答案 15 :(得分:1)
您也可以使用Bridge.net。从1.7版开始,它支持生成TypeScript C#类型的定义。见http://bridge.net/docs/generate-typescript-definitions/
答案 16 :(得分:1)
大家看看https://github.com/reinforced/ Reinforced.Typings 。我最近几天一直在使用typelite和t4模板,最终得到了这个项目。它非常简单,就像一个魅力。只需获取包,修改配置文件(就像10秒钟)并构建。所有这些都自动完成,没有任何问题保佑作者!
关于T4模板的坏处是,一旦你从VS构建,扫描的程序集就会被锁定,你必须重启VS(这有多愚蠢?)。在T4 Toolbox +一些VS清洁指令中有一些解决方法,但这些都不适用于我。
答案 17 :(得分:0)
如果您需要为每个生成的TypeScript类/接口创建一个单独的文件(即在每个文件的#34;单个类&#34;方式),您可以尝试TypeGen。它是一个可以从Package Manager控制台使用的工具,可以根据您的C#类/枚举生成TypeScript文件。它目前支持:
加上一些额外的功能。它也是开源的(您可以在github上查看)。
答案 18 :(得分:0)
如果有兴趣,您可以使用TypedRpc。它的目的不仅是在TypeScript中创建接口,而且使用JsonRpc协议在.Net中创建与服务的所有通信。
服务器中的类示例:
[TypedRpc.TypedRpcHandler]
public class RpcServerExample
{
public String HelloWorld()
{
return "Hello World!";
}
}
使用生成的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);
查看https://github.com/Rodris/TypedRpc以了解有关如何使用它的其他示例。
答案 19 :(得分:0)
我的解决方案是编写一个小的codegen util,它只需要一个项目程序集(并引用程序集)并开始扫描typescript和c#之间交互所涉及的类型。这个util输出两个javascript为d.ts ... 该工具在构建后事件中调用...就像一个魅力!
答案 20 :(得分:0)
ASP.NET Web API Client Generators比在SDLC期间昂首阔步的工具链和其他工具更方便,开销也更少。
尽管程序员通常使用WebApiClientGen生成客户端API代码,但该项目还提供POCO2TS.exe,这是一个命令行程序,可以从POCO类生成TypsScript接口。您可以使用Poco2ts.exe或poco2ts组件将代码生成与构建管道集成在一起。
答案 21 :(得分:0)
如果要通过node.js进行转换,则可以使用此程序包(csharp-to-typescript)。 https://www.npmjs.com/package/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;
}
答案 22 :(得分:0)
如果您使用的是VSCode,则可以查看该扩展名C# to TypeScript
看看如何通过单击即可从C#代码转换它。当然,并不是所有的类都将被转换,例如工厂,但是对于客户端来说已经足够了。我们只需要数据的形状。有时,如果我需要使用访客模式,我将用所需的其他方法来装饰。这样做只花了5秒钟。与上面的一分钟相比,我可以说这是一个很大的胜利。
在我的blog post
中查看更多详细信息答案 23 :(得分:0)
我在“开发者社区”页面上对此提出了功能请求:
正如您在此处的答案中看到的那样,有许多项目试图解决此问题,但是不幸的是,这些项目中有许多是单一开发人员项目,在此过程中不再得到支持。我认为,如果Microsoft维护其中一个项目,创建一个.NET
至TypeScript
生成器,那真的很整洁。
其中有些仍在维护,但严重依赖于单个开发人员:
TypeLite:
https://bitbucket.org/LukasKabrt/typelite/src/default/
TypeScript定义生成器:
https://marketplace.visualstudio.com/items?itemName=MadsKristensen.TypeScriptDefinitionGenerator
打字机:
https://github.com/frhagn/Typewriter
TypeGen:
https://github.com/jburzynski/TypeGen
增强型。
https://github.com/reinforced/Reinforced.Typings
我的提示是从一开始就选择一个您希望停止维护的项目,并准备切换到当前维护的项目。如果该项目不再受支持,我会选择使用注解并用手指交叉的东西,只要将其替换为其他标签即可。
答案 24 :(得分:0)
我使用一个小的自定义 DSL,它生成 TypeScript 和 C# DTO/POCO 类。 DSL 如下所示:
output CSharp
output TypeScript
namespace MyLibrary.Logic.DataModel.DTOs
class Something
property int ID
property string Name
property DateTime DateTime
property int ForeignKeyID
这样我就有了一个 DTO 模型,它有一个单一的源(DSL)并且位于两个 C# 和 TypeScript 逻辑模型之间。
然后我编写了一个 C# 和 TypeScript DTOFactory 类,该类具有负责在 DTO 和逻辑模型对象之间进行转换的方法。 (DTO 生成的一部分是序列化方法,它们转换为匿名 JSON 适合类型以进行序列化或存储在 IndexedDB 中。)
通过这样做,只要任何一方的模型发生变化,我就会收到编译错误,直到双方再次匹配。