Как повторно использовать существующие определения классов C# в проектах TypeScript



Я просто собираюсь начать использовать TypeScript в моем проекте клиента HTML, который принадлежит проекту MVC с моделью домена entity framework, уже есть. Я хочу, чтобы мои два проекта (на стороне клиента и на стороне сервера) были полностью разделены, так как две команды будут работать над этим... JSON и REST используются для передачи объектов вперед и назад.



конечно, мой domain объекты на стороне клиента должны совпадать с объектами на стороне сервера. В прошлом, я обычно делал это вручную. Есть способ повторного использования моих определений классов C# (особенно POJO классы в моей модели домена) для создания соответствующих классов в TypeScript"?

640   21  

21 ответов:

в настоящее время нет ничего, что будет сопоставлять C# с TypeScript. Если у вас есть много POCOs или вы думаете, что они могут часто меняться, вы можете создать конвертер - что-то простое по линиям...

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

до

export class MyPoco {
    public Name: string;
}

есть еще и обсуждение на Codeplex об автогенерации из C#.

просто чтобы держать вещи в курсе, TypeLite может генерировать интерфейсы TypeScript из C#:

http://type.litesolutions.net/

Web Essentials разрешить компилировать файлы C# в TypeScript .d.ts файлы при сохранении. Тогда вы можете ссылаться на определения из вашего .ts файлы.

enter image description here

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, добавьте расширение машинке.

Галерея Visual Studio

Сайт/Документация

обновление

с Web Essentials установленный в VS 2015, Вы можете щелкнуть правой кнопкой мыши файл класса, затем > Web Essentials > создать файл TypeScript Intellisense из контекстного меню.

Если вы используете vscode вы можете использовать мое расширение csharp2ts что делает именно это.

вы просто выбираете вставленный код C# и запускаете Convert C# to TypeScript команда из палитры команд enter image description here Пример преобразования:

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. Подробную документацию можно найти на сайте сайт проекта.

Попробовать Укрепить.Рамки типирования. Кажется, что это решает вашу проблему.

  1. установить с NuGet
  2. перейдите к своему 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; }
        }
    }
    
  3. перестроить проект
  4. узнайте сгенерированный код TypeScript в %Your_Project_Directory%/Scripts/project.ts файл и добавить его в проект вручную

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
    
  5. сделайте то же самое для всех ваших 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

    Ничего не найдено.