Как повторно использовать существующие определения классов C# в проектах TypeScript
Я просто собираюсь начать использовать TypeScript в моем проекте клиента HTML, который принадлежит проекту MVC с моделью домена entity framework, уже есть. Я хочу, чтобы мои два проекта (на стороне клиента и на стороне сервера) были полностью разделены, так как две команды будут работать над этим... JSON и REST используются для передачи объектов вперед и назад.
конечно, мой domain объекты на стороне клиента должны совпадать с объектами на стороне сервера. В прошлом, я обычно делал это вручную. Есть способ повторного использования моих определений классов C# (особенно POJO классы в моей модели домена) для создания соответствующих классов в TypeScript"?
21 ответов:
в настоящее время нет ничего, что будет сопоставлять C# с TypeScript. Если у вас есть много POCOs или вы думаете, что они могут часто меняться, вы можете создать конвертер - что-то простое по линиям...
public class MyPoco { public string Name { get; set; } }до
export class MyPoco { public Name: string; }есть еще и обсуждение на Codeplex об автогенерации из C#.
просто чтобы держать вещи в курсе, TypeLite может генерировать интерфейсы TypeScript из C#:
Web Essentials разрешить компилировать файлы C# в TypeScript
.d.tsфайлы при сохранении. Тогда вы можете ссылаться на определения из вашего.tsфайлы.
TypeLite и T4TSs выше обоих выглядели хорошо, просто выбрал один, TypeLite, раздвоил его, чтобы получить поддержку
- ValueTypes,
- Nullables
- camelCasing (TypeScript root doc использует camels, и это слишком хорошо сочетается с C#)
- общественные поля (любовь чистый и читаемый POCOs, также делает его легким для компилятора C#)
- отключить модуль
тогда мне нужен был C# интерфейсы и подумал, что пришло время испечь свою собственную вещь и написал простой скрипт T4, который просто делает то, что мне нужно. Она также включает в себя метки. РЕПО не требуется, только
использование
Нет библиотеки, нет NuGet, просто этот простой простой файл T4 - используйте "добавить элемент" в Visual Studio и выберите любой шаблон T4. Затем вставьте это в файл. Приспособьте каждую линию с "АКМЕ" внутри оно. Для каждого класса C# добавьте строку<#= Interface<Acme.Duck>() #>порядок имеет значение, любой известный тип будет использоваться в интерфейсах follwing. Если вы используете только интерфейсы, расширение файла может быть .д.ТС, для перечислений вам нужно .ТС file, так как создается экземпляр переменной.
настройки
Взломать скрипт.<#@ 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 класс.
вот мой подход к его решению. Объявите свои классы C# с атрибутом И.d.TS-файлы будут сгенерированы (с использованием преобразований T4). Там есть пакет на nuget а источник доступно на github. Я все еще работаю над проектом, но поддержка довольно обширная.
Если вы используете Visual Studio, добавьте расширение машинке.
обновление
с Web Essentials установленный в VS 2015, Вы можете щелкнуть правой кнопкой мыши файл класса, затем > Web Essentials > создать файл TypeScript Intellisense из контекстного меню.
Если вы используете 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; }
Я создал небольшую утилиту, которая может генерировать интерфейсы TypeScript из классов C#. Доступен как пакета NuGet. Подробную документацию можно найти на сайте сайт проекта.
Попробовать Укрепить.Рамки типирования. Кажется, что это решает вашу проблему.
- установить с NuGet
перейдите к своему POCO и добавьте выше
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; } } }- перестроить проект
узнайте сгенерированный код TypeScript в
%Your_Project_Directory%/Scripts/project.tsфайл и добавить его в проект вручнуюmodule YourNamespace { export interface IYourPoco { YourNumber: number; YourString: string; YourArray: string[]; YourDictionary: { [key: int]: any }; } }- сделайте то же самое для всех ваших POCOs и ссылки
project.tsв другом Код машинописного текста.Посмотреть подробнее документация wiki
у меня есть небольшое решение, которое использует шаблоны T4 (посмотреть источник).
вы идете от любого 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[]; }
вы можете использовать проект с открытым исходным кодом NSwag: в графическом интерфейсе можно выбрать класс .NET из существующей библиотеки DLL .NET и создать для него интерфейс TypeScript.
проект также предоставляет инструменты командной строки и поддержку шаблонов T4, а также генерацию клиентского кода для контроллеров Web API...
пожалуйста, взгляните на эту библиотеку пишущая машинка
Он охватывает не только классы, перечисления, интерфейсы и т. д., но и api-контроллеры, что просто потрясающе..
плюс он, как только вы сохраните источник .cs-файл, поэтому мне не нужно использовать какой-либо внешний инструмент для этого.. спасать.cs и вы обновляетесь .ТС
Как насчет наоборот?
проверить erecruit машинопись переводчика. Он поставляется с готовой поддержкой C#, но на самом деле основан на шаблонах (использует Nunjucks для рендеринга), что означает, что он может генерировать что - либо еще- VB.NET, F#, C++, XML, SQL - все, что вы можете кодировать с помощью шаблона.
работает как консольная программа .NET, программа NodeJS (для тех, кто не на Windows), или как расширение Visual Studio, в комплекте с генерировать при сохранении функциональности. И включает поддержку MSBuild, просто чтобы сделать ваш сервер сборки счастливым. : -)
вы также можете использовать это: https://github.com/pankleks/TypeScriptBuilder
эта небольшая библиотека генерирует определение типа TypeScript на основе типов C#. Используйте его непосредственно в вашем бэкэнд-проекте C# для создания кода для вашего проекта frontend TypeScript. Вы также можете написать небольшое консольное приложение, чтобы генерировать код с помощью инструментов предварительной сборки.
работает на полной и чистой основной структуре!
установить с NuGet для:
Install-Package TypeScriptBuilderподдерживаемые функции
- разрешение зависимостей типа
- дженериков
- наследование типа
- пространства имен (модули)
- метки
- типы с нулевым значением
- словарь converison (для сильного типа TS индексированных объектов)
- набор атрибутов управления генерацией кода
anyдля типов, которые не могут быть преобразованыбольше описание: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md
ребята взгляните на https://github.com/reinforced/усиленные. Typings. я играл с шаблонами typelite и t4 в течение последних нескольких дней и в конечном итоге с этим проектом. Это супер-простой и работает как шарм. Просто получить пакет, изменить файл конфигурации (это как в течение 10 секунд) и построить. Все делается автоматически без каких-либо проблем. Благослови автора!
плохая вещь о шаблонах T4 заключается в том, что как только вы строите из VS отсканированные сборки заблокированы, и вы должны перезапустить VS (насколько это глупо?). Есть некоторые обходные пути в T4 Toolbox + некоторые директивы VS cleaning, но ни один из них не работал для меня.
мне нравится решение citykid. Мне пришлось его немного продлить. Таким образом, решение также основано на методе codegeneration с шаблонами T4.
Он может генерировать общие типы TypeScript и внешние объявления.
Он поддерживает наследование и реализацию интерфейса.
поддерживает генераторы, массивы и списки в качестве полей типа.
также он преобразуется в Типы TypeScript, которые явно не упоминаются в конфигурации (например, мы импортируем введите A, а в выводе TS вы можете найти некоторые другие типы: типы полей, базовые типы и интерфейсы).
вы также можете переопределить имя типа.
перечисления также поддерживаются.
пример использования (вы можете найти его в репозитории проекта):
// 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
мне также очень понравился ответ @citykid, поэтому я расширил его, чтобы сделать целое пространство имен за раз. Просто поместите классы POCO в пространство имен и перестройте шаблоны T4. Я хотел бы знать, как создавать отдельные файлы для каждого, но это не конец света.
вы должны ссылаться на .DLL файлы в верхней части (где классы, которые вы хотите), и вы должны упомянуть пространства имен. Все строки для редактирования отмечены знаком ACME. Майор cudos в @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(); } #>
мое решение состояло в том, чтобы написать небольшой codegen util, который просто берет сборку проекта (и ссылки на сборки) и запускает типы сканирования, которые участвуют во взаимодействии между typescript и c#. Этот util выводит оба javascript как d .ts... Инструмент вызывается в событии после сборки ... работает как шарм!
Если вам нужно создать отдельный файл для каждого созданного класса/интерфейса TypeScript (т. е. в виде" одного класса на файл"), вы можете попробовать TypeGen. Это инструмент, который вы можете использовать из консоли диспетчера пакетов для создания файлов TypeScript на основе ваших классов/перечислений C#. В настоящее время он поддерживает:
- перечисления экспорт; Экспорт покос как классы или интерфейсы ц
- наследование
- универсального типа
- коллекция / вложенные типы коллекций
плюс некоторые дополнительные функции. Это также с открытым исходным кодом (вы можете проверить его на github).
Если интересно, вы можете использовать TypedRpc. Его целью является не только создание интерфейсов в TypeScript, но и создание всей связи со службой в .Net с использованием протокола JsonRpc.
пример для класса на сервере:
[TypedRpc.TypedRpcHandler] public class RpcServerExample { public String HelloWorld() { return "Hello World!"; } }использование сгенерированного кода машинописи:
/// <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 для других примеров того, как его использовать.
вы также можете использовать Bridge.net. начиная с версии 1.7 он поддерживает генерацию машинописного текста определения для типов C#. См.http://bridge.net/docs/generate-typescript-definitions/
Если кто-то хочет веб-решение я сделал простой сайт, который будет конвертировать классы C# DTO в интерфейсы Typescript. http://dto2ts.ga/ (это еще не ошибка бесплатно Тхо)


Comments