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

121

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

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

pabloelustondo
источник
Нет, это совершенно разные языки.
Ханс Пассан
6
Да, я имел в виду POCO. Я знаю, что это совершенно разные языки ... но было бы здорово как-то не набирать все это снова ... Я думаю, что на данный момент я скопирую и вставлю свой код на C # ... и буду реорганизовать все, пока он не скомпилируется в TypeScript ... таким образом я уверен, что не ввел атрибут неправильно ... и помогите мне не запоминать. Но это явно звучит не очень хорошо.
pabloelustondo
1
Меня также интересует другое направление - от классов модели Typescript до классов модели C #, двунаправленно десериализуемых через JSON.
Джейсон Клебан
Я искал то же самое, и я только что наткнулся на этот плагин Gulp, хотя я не знаю, насколько он поддерживается
Люк Т О'Брайен

Ответы:

54

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

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

к

export class MyPoco {
    public Name: string;
}

На Codeplex также обсуждается автоматическая генерация из C # .

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

http://type.litesolutions.net/

Фентон
источник
Да, вот что я думал сделать вручную ... хорошо. Не уверен, что у меня есть время написать конвертер сейчас ... но да, это была бы идея. Спасибо. Может быть, Microsoft просто делает это прямо сейчас, пока мы говорим.
pabloelustondo
Вы можете сделать это любым из двух способов ... Сгенерируйте файлы .ts с помощью EnvDTE Automation или с помощью Mef Directory Catalog и Reflection / Introspection. Я предпочитаю второй, так как он не зависит от визуального оформления.
Арек Бал
1
Я начал использовать дротик и столкнулся с той же проблемой, как делить типы между C # и javascript. Я надеялся, что эта проблема будет решена с помощью Typescript, но похоже, что ответа еще нет. Меня также немного испортил компилятор saltarelle, который довольно красиво скомпилировал c # в javascript saltarelle-compiler.com
BraveNewMath
2
@DanielHarvey Enums правильно генерирует TypeLite, вероятно, исправлено с момента вашего комментария.
pauloya 08
1
TypeScriptPaste преобразует C # в TypeScript. У вас должен быть компилятор Roslyn, что означает Visual Studio 2015, но он работает очень хорошо. Это не на 100%, и мне пришлось немного упростить некоторый код - например, преобразовать foreach в циклы for, написать свой собственный класс List <T>, и вам нужно структурировать свой код так, чтобы он был `` чистым '', только с внутренними структурами данных для работать, но это была небольшая плата за возможность изменять и тестировать код в VS, а затем «компилировать» его в Javascript через TypeScript.
Джон Мотт,
37

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

введите описание изображения здесь

VB
источник
Вы, может быть, путаете файлы .ts и C #? не могу найти этот вариант
Флорес
1
Но после некоторого поиска я нашел то, что вы имеете в виду ... на самом деле весьма полезно.
Flores
2
На самом деле это не так, я совершенно уверен, что операция ничего не «компилирует», она генерирует код. Это смутило меня, но это неверно на 99%, я дам вам это ;-)
Флорес
1
Было бы более полезно, если бы мы могли настроить его пространство имен и некоторые настройки.
Фаршид Сабери
5
Теперь вам нужно установить «TypeScript Definition Generator» вручную, чтобы использовать это - см. Здесь
Таран,
25

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

  • ValueTypes ,
  • Nullables
  • camelCasing (в корневом документе TypeScript используются верблюды, и это слишком хорошо сочетается с C #)
  • общедоступные поля (люблю чистые и читаемые POCO, которые также упрощают компилятор C #)
  • отключить генерацию модуля

Затем мне понадобились интерфейсы 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.

citykid
источник
1
Очень мило, но у вас typeof(ParticleKind)в вашем Enum<T>. Неужто так должно быть typeof(T)? Также вам необходимо обновить boolдо более booleanпоздних версий TypeScript
Gone Coding
Также обратите внимание: это нарушает свойства перечисления в классах
Gone Coding
1. Эээ, да, спасибо за внимание, исправлю. 2. Машинопись эволюционировала с момента написания этого сценария. Похоже, ему может потребоваться обновление, вы, безусловно, правы. Надеюсь, это послужит отправной точкой для ваших собственных целей.
citykid
1
Я преобразовал его во вспомогательную библиотеку, исправил ошибки и теперь использую только однострочные записи в моих файлах TT для создания интерфейсов и новых const enums. Спасибо за пример. Это было очень близко, и я получил рабочий результат намного быстрее.
Gone Coding
25

Если вы используете 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;
}
Рафаэль
источник
1
Я думаю, что люди заинтересованы в полностью автоматизированном решении, в котором компиляция с помощью msbuild выводит определения машинописного текста до их использования. Есть ли способ получить это с помощью плагина?
binki
Нет, это плагин VSCode, работает только внутри редактора, а не как отдельная программа
Rafael
Я преобразовал это расширение vscode в модуль узла, если вы хотите, через node.js или tytpescript. Вы можете проверить репо здесь: github.com/YuvrajSagarRana/csharp-to-typescript
Сагар Рана Магар
Есть ли способ связать команду с сочетанием клавиш? Например, можно выбрать код .net и настроить shift + ctrl + c для выполнения «Преобразовать C # в TypeScript», что ускорит процесс. Плагин нравится!
Павел Чох,
18

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

Кристоффер
источник
Я форкнул этот проект и добавил поддержку нокаутов здесь: github.com/omniscient/t4ts
Frank.Germain
18

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

Галерея Visual Studio

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

Обновить

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

Майкл Транчида
источник
Он создал файл d.ts для поддержки intellisense, но как его использовать, а затем какова связь с созданием базовых классов? Я зашел в WebEssentails и не нашел никаких документов. Какие-либо предложения? ТИА.
Howardlo
@hio Просто укажите файл d.ts в любом файле .ts, в котором вы должны его использовать. Когда / если вы измените свой класс, базовый файл d.ts обновится (Совет: вы можете перетащить d.ts файл в начало файла .ts, и он создаст для вас ссылку.)
Майкл Транчида
Спасибо, Майкл! Я попытался перетащить файл d.ts в пустой файл .ts, и он только добавил файл в содержащую папку. Пробовал в VS2015 и новом VS2017, результаты те же. Вы знаете какое-нибудь видео или учебное пособие, показывающее, как все это работает? TIA
Howardlo
Я попытался перетащить файл d.ts в файл .ts в редакторе, и он создал ссылку. Думаю, теперь я понимаю. Учебник по-прежнему приветствуется. Спасибо!
Howardlo
@hlo Извините, я не знаю никаких руководств. Когда я иду по маршруту Web Essentials, я делаю дубликаты файлов .ts, потому что мои модели домена находятся в библиотеке классов, а в моем коде на стороне клиента мне обычно нужно создавать модели dto или vm, которые браузер должен знать о. Если у вас возникли проблемы со всем этим, попробуйте расширение «Пишущая машинка». Это позволяет вам иметь файлы моделей ur .ts где угодно.
Майкл Транчида
15

Попробуйте фреймворк 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.

См. Более подробную информацию в документации wiki

Павел Борисович Новиков
источник
Добавьте образец фрагмента, а не используйте только URL-адрес
Venkat.R
Есть вики с быстрым стартом О_о github.com/reinformed/Reinformed.Typings/wiki
Павел Б. Новиков
Но ладно, я приведу здесь пример :)
Павел Б. Новиков
Нет поддержки .NET Core ( ссылка ) :(
Alex Klaus
5
Поддержка @AlexKlaus .NET Core прибыла
Павел Б. Новиков
10

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

Лукас Кабрт
источник
Хорошая библиотека, хорошие усилия. К сожалению, не удалось найти способ настроить производимый тип значения, например, преобразовать «строку» в «KnockoutObservableString» или «строку []» в «KnockoutObservableArray». Это то, что есть в планах на ближайшее будущее?
root
2
Я использовал T4TS, это заняло 14 минут.
root
@root, вам не следует фактически преобразовывать строку [] в KnockoutObservableArray в сгенерированных определениях. Вы используете эти сгенерированные определения, когда фактически потребляете сериализованные экземпляры этих классов. Теперь вы можете запускать весь объект через плагин сопоставления, но я бы предостерегал от этого. Это может быть чрезмерно, и я всегда предпочитаю вручную преобразовывать (возможно, через ctor) из ваших сериализованных экземпляров классов C # в классы TypeScript, полные наблюдаемых. Единственный раз, когда я предлагаю слепо запустить его через плагин сопоставления, - это приложение в стиле CRUD.
Аллен Райс
@Lukas Kabrt, большое спасибо за TypeLite, он был чрезвычайно полезен в наших проектах, и я кратко написал об этом здесь: underwatergorilladome.com/…
Аллен Райс
@AllenRice, мне было удобно иметь вручную созданные классы модели представления Knockout, где я использую описанный вами подход и комбинирую его с автоматически сгенерированным классом, который представляет собой DTO, который был передан через плагин сопоставления.
root
7

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

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

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

harishr
источник
Замечательно то, что он создает файлы TS при сохранении соответствующих файлов CS. Однако настройка выходной папки довольно хрупкая (см. Обсуждение здесь ) и требует некоторого кода, созданного вручную.
Alex Klaus
Я действительно использую для этого инструменты сборки, и этот инструмент намного мощнее, чем многие из упомянутых здесь
harishr 01
6

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

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

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

Рико Сутер
источник
Имейте в виду, что NSwag будет генерировать файлы TS только либо из двоичных файлов (вам также нужны все зависимости ваших двоичных файлов), либо из своего файла JSON, который Swagger создает из двоичных файлов.
Alex Klaus
3

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

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

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

JB1
источник
2

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

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

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

Федор Сойкин
источник
Я поставил +1, потому что это действительно лучшее решение, а генерация при сохранении - убойная функция.
Джон Заброски
2

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

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

Работает на платформе Full & NET Core!

Установить с помощью nuget: Install-Package TypeScriptBuilder

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

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

Подробное описание: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md

pankleks
источник
Просто попробовал, TypeScriptBuilderи пока все выглядит отлично; ура для поддержки .NET Core!
Tobias J
1

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

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

VEB
источник
1

Мне нравится решение 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

Катящийся камень
источник
1

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

Джордж Бирбилис
источник
Основная проблема заключается в том, что когда вы выполняете поиск в Google, вы можете найти исходный вопрос и никогда не заметить, что полезная информация существует в дублированном / закрытом ответе
Джордж Бирбилис,
Так что убедитесь, что каноническое издание содержит самую лучшую информацию. Не отправляйте сразу несколько вопросов. Любое дальнейшее обсуждение этого вопроса можно перенести в Meta Stack Overflow
Мартейн Питерс
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();
    }
#>
user230910
источник
0

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

Paul0515
источник
Я не знаю, почему я решил выводить js и d.ts ... прямо сейчас он просто выводит .ts ... просто и очень выполнимо.
Paul0515
1
Привет, Пол ... не могли бы вы поделиться с нами кодом утилиты. Я хочу просто создать файл ts (в определенном месте папки), а не файл d.ts, как вы сказали. Это то, что может делать ваша утилита ??
Кришна Теджа Вирамачанени,
0

Если интересно, можете использовать 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, чтобы узнать о других примерах его использования.

Rodris
источник
0

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

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

ZZZ
источник
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;
}
Сагар Рана Магар
источник
0

Если вы используете VSCode, вы можете взглянуть на это расширение C # для TypeScript

Преобразование класса C # в интерфейс TypeScript

Посмотрите, как я могу преобразовать его из кода C # одним щелчком мыши. Конечно, не все классы будут преобразованы, например фабрика, но для клиентской стороны этого достаточно. Нам просто нужна форма данных. Иногда, если мне нужно использовать шаблон посетителя, я украсю дополнительными методами, которые мне нужны. Это заняло всего 5 секунд. По сравнению с одной минутой выше, я могу сказать, что это большая победа.

Подробнее см. В моем сообщении в блоге

trungk18
источник