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

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

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

Ответ 1

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

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

Для

export class MyPoco {
    public Name: string;
}

Существует также обсуждение Codeplex об автогенерации с С#.

Чтобы обновить содержимое, TypeLite может генерировать TypeScript интерфейсы из С#:

http://type.litesolutions.net/

Ответ 2

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

enter image description here

Ответ 3

TypeLite и T4TSs выше обоих выглядели хорошо, просто выбрали один, TypeLite, разветкили его, чтобы получить поддержку

  • типы значений
  • Nullables
  • camelCasing (TypeScript root doc использует верблюдов, и это слишком хорошо сочетается с С#)
  • публичные поля (люблю чистые и читаемые POCOs, также упрощает компилятор С#)
  • отключить создание модуля

Тогда мне нужны С# интерфейсы и подумал, что пришло время испечь мою собственную вещь и написал простой T4 script, который просто делает то, что мне нужно. Он также включает Enums. Репо не требуется, 100 строк T4.

Использование
Нет библиотеки, нет NuGet, просто этот простой простой файл T4 - используйте "добавить элемент" в Visual Studio и выберите любой шаблон T4. Затем вставьте это в файл. Адаптируйте каждую строку с помощью "ACME". Для каждого класса С# добавьте строку

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

Вопросы заказа, любой известный тип будет использоваться в интерфейсах follwing. Если вы используете только интерфейсы, расширение файла может быть .d.ts, для перечислений вам нужен файл .ts, поскольку создается экземпляр переменной.

Настройка
Взломайте 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();
    }
#>

Следующим уровнем script будет создание интерфейса службы из класса MVC JsonController.

Ответ 4

Вот мой подход к его решению. Объявите свои классы С# с атрибутом, и будут создаваться файлы .d.ts(с использованием преобразований T4). Там пакет на nuget, а источник доступен в github. Я все еще работаю над проектом, но поддержка довольно обширна.

Ответ 5

Если вы используете Visual Studio, добавьте расширение Typewriter.

Галерея Visual Studio

Веб-сайт/Документация

Обновление

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

Ответ 6

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

Вы просто выберите вложенный код С# и запустите команду 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;
}

Ответ 7

Попробуйте фреймворк Reinforced.Typings. Кажется, это решает твою проблему.

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

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
    
  5. Сделайте то же самое для всех ваших POCO и ссылочных project.ts в вашем другом коде TypeScript.

Подробности смотрите в вики документации

Ответ 8

Я создал небольшую утилиту, которая может генерировать интерфейсы TypeScript из классов С#. Доступно как пакет NuGet. Подробную документацию можно найти на веб-странице проекта .

Ответ 9

Пожалуйста, посмотрите на эту библиотеку Пишущая машинка

Он конвертирует не только классы, перечисления, интерфейсы и т.д., Но также и api-контроллеры, что просто потрясающе..

Кроме того, он делает это, как только вы сохраняете исходный файл .cs, поэтому мне не нужно запускать какие-либо внешние инструменты. Сохраните .cs, и вы получите обновленный .ts

Ответ 10

У меня есть небольшое решение, которое использует шаблоны 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[];
}

Ответ 11

Вы можете использовать проект с открытым исходным кодом NSwag: в графическом интерфейсе вы можете выбрать .NET-класс из существующей .NET DLL и создать для него интерфейс TypeScript.

Проект также предоставляет инструменты командной строки и поддерживает шаблоны T4, а также создает клиентский код для контроллеров Web API...

Ответ 12

Как насчет другого пути?

Отъезд erecruit TypeScript Переводчик. Он поставляется с готовой поддержкой С#, но на самом деле основан на шаблонах (использует Nunjucks для рендеринга), что означает, что он может генерировать что-нибудь еще - VB.NET, F #, С++, XML, SQL - все, что вы можете кодировать с помощью шаблона.

Работает как консольная программа .NET, NodeJS-программа (для тех, кто не находится в Windows), или как расширение Visual Studio, в комплекте сгенерировать -экономическая функциональность. И включает поддержку MSBuild, просто чтобы сделать ваш сервер сборки счастливым.: -)

Ответ 13

Вы также можете использовать это: https://github.com/pankleks/TypeScriptBuilder

Эта небольшая библиотека генерирует определение типа TypeScript на основе типов С#. Используйте его непосредственно в своем проекте С# для создания кода для вашего внешнего проекта TypeScript. Вы также можете использовать небольшое консольное приложение для генерации кода с помощью инструментов предварительной сборки.

Работает на основе Full и NET Core!

Установить nuget: Install-Package TypeScriptBuilder

Поддерживаемые функции

  • Разрешение зависимости типа
  • Дженерики
  • Наследование типов
  • Пространства имен (модули)
  • Перечисления
  • Невозможные типы
  • Перекодирование словаря (для индексированных объектов с сильным типом TS)
  • Набор атрибутов управления генерированием кода
  • any для типов, которые невозможно преобразовать

Больше описания: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md

Ответ 14

Ребята взгляните на https://github.com/reinforced/ Reinforced.Typings. Последние несколько дней я играю с шаблонами и t4-шаблонами и заканчиваю этот проект. Это супер-просто и работает как шарм. Просто получите пакет, измените файл конфигурации (это похоже на 10 секунд) и выполните сборку. Все делается автоматически без каких-либо проблем. Благословите автора!

Плохая вещь о шаблонах T4 заключается в том, что после сборки из VS отсканированные сборки блокируются, и вы должны перезапустить VS (как это глупо?). В T4 Toolbox + есть некоторые способы очистки VS, но ни один из них не работал у меня.

Ответ 15

Мне нравится решение citykid. Я немного расширил его. Таким образом, решение также основано на методе когенерации с шаблонами 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

Ответ 16

Если вам нужно создать отдельный файл для каждого сгенерированного класса TypeScript class/interface (т.е. в стиле "один класс для каждого файла" ), вы можете попробовать TypeGen. Это инструмент, который вы можете использовать из консоли диспетчера пакетов для генерации файлов TypeScript на основе ваших классов/перечислений С#. В настоящее время он поддерживает:

  • экспорт перечислений; экспорт POCOs в качестве классов TS или интерфейсов.
  • наследование
  • общие типы
  • коллекции/вложенные типы коллекций

плюс некоторые дополнительные функции. Он также с открытым исходным кодом (вы можете проверить его на github).

Ответ 17

Вы также можете использовать Bridge.net. Начиная с версии 1.7 он поддерживает генерацию TypeScript определения типов С#. См. http://bridge.net/docs/generate-typescript-definitions/

Ответ 18

Мне также понравился @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();
    }
#>

Ответ 19

Моим решением было написать небольшую утилиту codegen, которая просто берет сборку проекта (и ссылается на сборки) и запускает типы сканирования, которые участвуют во взаимодействии между typescript и С#. Это использование выводит как javascript как d.ts... Инструмент вызывается в событии post-build... работает как шарм!

Ответ 20

Если вы заинтересованы, вы можете использовать TypedRpc. Его целью является не только создание интерфейсов в TypeScript, но и создание всей связи с сервисом в .Net с использованием протокола JsonRpc.

Пример для класса на сервере:

[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 для других примеров того, как его использовать.

Ответ 21

Если кому-то нужно веб-решение, я сделал простой сайт, который будет конвертировать классы С# DTO в интерфейсы Typescript. http://dto2ts.ga/ (пока не без ошибок)

Ответ 22

Генераторы клиентов ASP.NET Web API могут быть более удобными, менее затратными, чем сваггерский набор инструментов и другие во время SDLC.

Хотя программисты обычно используют WebApiClientGen для генерации клиентских API-кодов, этот проект также предоставляет POCO2TS.exe, программу командной строки, которая генерирует интерфейсы TypsScript из классов POCO. Вы можете использовать Poco2ts.exe или компонент poco2ts для интеграции генерации кода с вашим конвейером сборки.

Ответ 23

Если вы хотите преобразовать его через node.js, вы можете использовать этот пакет (csharp-to-typcript). https://www.npmjs.com/package/csharp-to-typescript

Sourcode: 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;
}