So verwenden Sie vorhandene C # -Klassendefinitionen in TypeScript-Projekten wieder

126

Ich werde gerade mit der Verwendung TypeScriptin meinem HTML-Client-Projekt beginnen, das zu einem MVC-Projekt gehört, in dem bereits ein Entity-Framework-Domänenmodell vorhanden ist. Ich möchte, dass meine beiden Projekte (Client- und Serverseite) vollständig getrennt sind, da zwei Teams daran arbeiten werden ... JSON und REST werden verwendet, um Objekte hin und her zu kommunizieren.

Natürlich sollten meine domainObjekte auf der Clientseite mit den Objekten auf der Serverseite übereinstimmen. In der Vergangenheit habe ich dies normalerweise manuell gemacht. Gibt es eine Möglichkeit, meine C # POJO-Klassendefinitionen (insbesondere der Klassen in meinem Domänenmodell) wiederzuverwenden , um die entsprechenden Klassen in TypeScript zu erstellen?

pabloelustondo
quelle
Nein, sie sind völlig verschiedene Sprachen.
Hans Passant
6
Ja, ich meinte POCO. Ich weiß, dass es sich um völlig unterschiedliche Sprachen handelt ... aber es wäre großartig, das alles irgendwie nicht erneut zu tippen ... Ich denke, dass ich im Moment meinen C # -Code kopieren und einfügen werde .. und alles umgestalten werde, bis es kompiliert ist TypeScript ... auf diese Weise bin ich sicher, dass ich kein falsches Attribut eingegeben habe ... und helfe mir, mich nicht erinnern zu müssen. Aber das hört sich eindeutig überhaupt nicht gut an.
Pabloelustondo
1
Ich interessiere mich auch für die andere Richtung - von Typescript-Modellklassen bis zu C # -Modellklassen, die über JSON bidirektional deserialisierbar sind.
Jason Kleban
Ich habe nach dem gleichen gesucht und bin gerade auf dieses Gulp-Plugin gestoßen, obwohl ich nicht weiß, wie viel Unterstützung es dafür gibt
Luke T O'Brien

Antworten:

56

Derzeit gibt es nichts, was C # TypeScript zuordnen könnte. Wenn Sie viele POCOs haben oder glauben, dass sie sich häufig ändern, können Sie einen Konverter erstellen - etwas Einfaches im Sinne von ...

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

Zu

export class MyPoco {
    public Name: string;
}

Es gibt auch eine Diskussion über Codeplex über die automatische Generierung aus C # .

Um die Dinge auf dem neuesten Stand zu halten, kann TypeLite TypeScript-Schnittstellen aus C # generieren:

http://type.litesolutions.net/

Fenton
quelle
Ja, so etwas habe ich mir manuell überlegt. Gut. Ich bin mir nicht sicher, ob ich jetzt Zeit habe, einen Konverter zu schreiben. Aber ja, das wäre die Idee. Vielen Dank. Vielleicht macht Microsoft es gerade, während wir sprechen.
Pabloelustondo
Sie können dies auf zwei Arten tun: Generieren Sie .ts-Dateien mit Hilfe von EnvDTE Automation oder mithilfe von Mef Directory Catalog und Reflection / Introspection. Ich bevorzuge die zweite, da sie nicht vom visuellen abhängt.
Arek Bal
1
Ich fing an, Dart zu verwenden und stieß auf die gleiche Frage, wie man Typen zwischen c # und Javascript teilt. Ich hoffte, dass dieses Problem durch Typescript gelöst wird, aber es sieht so aus, als ob die Antwort noch nicht vorliegt. Ich war auch ein bisschen verwöhnt von dem Saltarelle-Compiler, der c # zu Javascript kompiliert hat. Saltarelle-compiler.com
BraveNewMath
2
@ DanielHarvey-Aufzählungen werden von TypeLite korrekt generiert, wahrscheinlich seit Ihrem Kommentar behoben.
Pauloya
1
TypeScriptPaste konvertiert C # in Typescript. Sie müssen den Roslyn-Compiler haben, was Visual Studio 2015 bedeutet, aber er funktioniert wirklich gut. Es ist nicht 100% und ich musste etwas Code dumm machen - foreach in for-Schleifen konvertieren, zum Beispiel meine eigene List <T> -Klasse schreiben, und Sie müssen Ihren Code so strukturieren, dass er 'rein' ist und nur interne Datenstrukturen enthält arbeiten mit, aber es war ein kleiner Preis, um Code in VS zu ändern und zu testen und ihn dann über TypeScript in Javascript zu "kompilieren".
John Mott
38

Mit Web Essentials können C # -Dateien beim Speichern in TypeScript- .d.tsDateien kompiliert werden . Dann könnten Sie auf die Definitionen aus Ihren .tsDateien verweisen .

Geben Sie hier die Bildbeschreibung ein

VB
quelle
Verwechseln Sie vielleicht .ts-Dateien und c #? kann diese Option nicht finden
Flores
1
Aber nach einigem Suchen habe ich gefunden, was du meinst ... eigentlich ganz nützlich.
Flores
2
Eigentlich nicht, ich bin mir ziemlich sicher, dass die Operation nichts "kompiliert", sondern Code generiert. Das hat mich verwirrt, ist aber nicht zu 99% richtig, das gebe ich dir ;-)
Flores
1
Es war nützlicher, wenn wir den Namespace konfigurieren und einige Anpassungen vornehmen konnten.
Farshid Saberi
5
Sie müssen jetzt 'TypeScript Definition Generator' manuell installieren, um dies zu verwenden - siehe hier
Taran
27

TypeLite und T4TSs oben sahen beide gut aus. Sie haben nur einen ausgewählt, TypeLite, und ihn gegabelt, um Unterstützung zu erhalten

  • ValueTypes ,
  • Nullables
  • camelCasing (TypeScript- Stammdokument verwendet Kamele, und dies passt zu gut zu C #)
  • öffentliche Felder (lieben saubere und lesbare POCOs, machen es auch dem C # -Compiler leicht)
  • Modulgenerierung deaktivieren

Dann brauchte ich C # -Schnittstellen und dachte, es sei Zeit, mein eigenes Ding zu backen, und schrieb ein einfaches T4-Skript, das genau das tut, was ich brauche. Es enthält auch Aufzählungen . Kein Repo erforderlich, nur <100 Zeilen T4.

Verwendung
Keine Bibliothek, kein NuGet, nur diese einfache T4-Datei - verwenden Sie "Element hinzufügen" in Visual Studio und wählen Sie eine beliebige T4-Vorlage aus. Fügen Sie dies dann in die Datei ein. Passen Sie jede Zeile mit "ACME" an. Fügen Sie für jede C # -Klasse eine Zeile hinzu

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

In Bezug auf die Reihenfolge wird jeder bekannte Typ in den folgenden Schnittstellen verwendet. Wenn Sie nur Schnittstellen verwenden, kann die Dateierweiterung .d.ts sein . Für Aufzählungen benötigen Sie eine .ts- Datei, da eine Variable instanziiert wird.

Anpassung
Hacken Sie das Skript.

<#@ 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();
    }
#>

Die nächste Ebene des Skripts besteht darin, die Dienstschnittstelle aus der MVC JsonController-Klasse zu erstellen.

Stadtkind
quelle
1
Sehr süß, aber du hast typeof(ParticleKind)in deinem Enum<T>. Sicher sollte das sein typeof(T)? Außerdem müssen Sie ein Update durchführen bool, um booleanfür spätere TypeScript-Versionen verfügbar zu sein
Gone Coding,
Beachten Sie auch: Dies bricht mit Enum-Eigenschaften in Klassen
Gone Coding
1. Ähh, ja, danke für das Heads-up, werde das beheben. 2. Typoskript hat sich seit dem Schreiben dieses Skripts entwickelt. Sieht so aus, als könnte es ein Update brauchen, Sie haben sicherlich Recht. Ich hoffe, es dient als Ausgangspunkt für Ihren eigenen Zweck.
Citykid
1
Ich habe es in eine Hilfsbibliothek konvertiert, die Fehler behoben und verwende jetzt nur einzeilige Einträge in meinen TT-Dateien, um Schnittstellen und die neuen const enums zu generieren . Danke für das Beispiel. Es war sehr eng und brachte mir viel schneller ein Arbeitsergebnis.
Gone Coding
27

Wenn Sie vscode verwenden, können Sie meine Erweiterung csharp2ts verwenden die genau das tut.

Sie wählen einfach den eingefügten C # -Code aus und führen den Convert C# to TypeScriptBefehl über die Befehlspalette aus. Geben Sie hier die Bildbeschreibung ein Ein Konvertierungsbeispiel:

public class Person
{
    /// <summary>
    /// Primary key
    /// </summary>
    public int Id { get; set; }

    /// <summary>
    /// Person name
    /// </summary>
    public string Name { get; set; }
}

zu

export interface Person
{
    /**Primary key */
    Id : number;

    /**Person name */
    Name : string;
}
Rafael
quelle
1
Ich denke, die Leute sind an einer vollautomatisierten Lösung interessiert, bei der das Kompilieren mit msbuild die Typoskriptdefinitionen ausgibt, bevor sie verbraucht werden. Gibt es eine Möglichkeit, dies mit dem Plugin zu erreichen?
Binki
Nein, dies ist ein VSCode-Plugin, funktioniert nur im Editor und nicht als eigenständiges Programm
Rafael
Ich habe diese vscode-Erweiterung in ein Knotenmodul konvertiert, wenn Sie über node.js oder tytpescript möchten. Sie können das Repo hier überprüfen: github.com/YuvrajSagarRana/csharp-to-typescript
Sagar Rana Magar
Gibt es eine Möglichkeit, Befehle mit Tastenkombinationen zu verknüpfen? Wenn Sie beispielsweise .net-Code auswählen und Setup-Umschalt + Strg + C ausführen, um "C # in TypeScript konvertieren" auszuführen, würde dies den Prozess beschleunigen. Ich mag das Plugin!
Pawel Cioch
19

Wenn Sie Visual Studio verwenden, fügen Sie die Typewriter-Erweiterung hinzu.

Visual Studio-Galerie

Website / Dokumentation

Aktualisieren

Wenn Web Essentials in VS 2015 installiert ist, können Sie mit der rechten Maustaste auf die Klassendatei klicken und dann im Kontextmenü auf> Web Essentials> Typescript Intellisense-Datei erstellen.

Michael Tranchida
quelle
Es wurde die Datei d.ts für die Intellisense-Unterstützung erstellt, aber wie wird sie verwendet und in welcher Beziehung steht sie dann zum Erstellen der zugrunde liegenden Klassen? Ich bin zu den WebEssentails gegangen und kann keine Dokumente finden. Irgendwelche Vorschläge? TIA.
Howardlo
@hio Verweisen Sie einfach auf die d.ts-Datei in der .ts-Datei, in der Sie sie verwenden möchten. Wenn Sie Ihre Klasse ändern, wird die zugrunde liegende d.ts-Datei aktualisiert. (Tipp: Sie können die d.ts ziehen und ablegen Datei an den Anfang einer .ts-Datei und es wird die Referenz für Sie erstellen.)
Michael Tranchida
Danke Michael! Ich habe versucht, die Datei d.ts in eine leere .ts-Datei abzulegen, und die Datei wurde nur dem enthaltenen Ordner hinzugefügt. Versuchte es in VS2015 und dem neuen VS2017, gleiche Ergebnisse. Kennen Sie ein Video oder Tutorial, das zeigt, wie das alles funktioniert? TIA
Howardlo
Ich habe versucht, die Datei d.ts im Editor in eine .ts-Datei abzulegen, und sie hat die Referenz erstellt. Ich glaube ich verstehe jetzt. Tutorial noch willkommen. Vielen Dank!
Howardlo
@hlo Sorry, kenne keine Tutorials. Wenn ich mich für Web Essentials entscheide, habe ich doppelte .ts-Dateien erstellt, da sich meine Domänenmodelle in einer Klassenbibliothek befinden. In meinem clientseitigen Code muss ich normalerweise die dto- oder vm-Modelle erstellen, die der Browser benötigt wissen über. Wenn Sie Probleme damit haben, versuchen Sie es mit der Schreibmaschinenerweiterung. Sie können Ihre .ts-Modelldateien überall dort haben, wo Sie möchten.
Michael Tranchida
18

Hier ist mein Lösungsansatz. Deklarieren Sie Ihre C # -Klassen mit einem Attribut und .d.ts-Dateien werden generiert (mithilfe von T4-Transformationen). Es gibt ein Paket auf Nuget und die Quelle ist auf Github verfügbar . Ich arbeite noch an dem Projekt, aber der Support ist ziemlich umfangreich.

Christoffer
quelle
Ich gabelte dieses Projekt und fügte Knockout-Unterstützung hier hinzu: github.com/omniscient/t4ts
Frank.Germain
15

Versuchen Sie es mit dem Reinforced.Typings-Framework. Scheint, dass es Ihr Problem löst.

  1. Installieren Sie es von NuGet
  2. Navigieren Sie zu Ihrem POCO und fügen Sie darüber ein [TsInterface]Attribut hinzu

    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. Erstellen Sie Ihr Projekt neu
  4. Finden Sie den generierten TypeScript-Code in der %Your_Project_Directory%/Scripts/project.tsDatei heraus und fügen Sie ihn manuell zum Projekt hinzu

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
    
  5. Machen Sie dasselbe für alle Ihre POCOs und Verweise project.tsin Ihrem anderen TypeScript-Code.

Weitere Details finden Sie im Dokumentations-Wiki

Pavel B. Novikov
quelle
Fügen Sie das Beispiel-Snippet hinzu und teilen Sie nur die URL
Venkat.R
Es gibt Wiki mit Schnellstart O_o github.com/reinforced/Reinforced.Typings/wiki
Pavel B. Novikov
Aber ok, ich werde hier ein Beispiel geben :)
Pavel B. Novikov
Keine .NET Core-Unterstützung ( Link ) :(
Alex Klaus
6
@AlexKlaus .NET Core-Unterstützung ist eingetroffen
Pavel B. Novikov
10

Ich habe ein kleines Dienstprogramm erstellt, das TypeScript-Schnittstellen aus C # -Klassen generieren kann. Ist als NuGet-Paket erhältlich . Eine ausführliche Dokumentation finden Sie auf der Projektwebseite .

Lukas Kabrt
quelle
Schöne Bibliothek, gute Mühe. Leider wurde keine Möglichkeit gefunden, den produzierten Werttyp zu optimieren, z. B. "string" in "KnockoutObservableString" oder "string []" in "KnockoutObservableArray" zu konvertieren. Ist es etwas, das in naher Zukunft geplant ist?
Wurzel
2
Ich habe stattdessen T4TS verwendet, es dauerte 14 Minuten.
Wurzel
@root, Sie sollten in den generierten Definitionen eigentlich keinen String [] in KnockoutObservableArray konvertieren. Sie verwenden diese generierten Definitionen, wenn Sie die serialisierten Instanzen dieser Klassen tatsächlich verwenden. Jetzt führen Sie möglicherweise das gesamte Objekt über das Mapping-Plugin aus, aber ich würde davor warnen. Das kann übertrieben sein und ich bevorzuge es immer, manuell (wahrscheinlich über einen Ctor) von Ihren serialisierten Instanzen von C # -Klassen in TypeScript-Klassen voller Observablen zu konvertieren. Das einzige Mal, dass ich vorschlagen würde, es blind über das Mapping-Plugin auszuführen, ist für eine CRUD-App.
Allen Rice
@ Lukas Kabrt, vielen Dank für TypeLite, es war äußerst nützlich in unseren Projekten und ich habe hier kurz darüber geschrieben: underwatergorilladome.com/…
Allen Rice
@AllenRice, ich fand es praktisch, handgefertigte Knockout-Ansichtsmodellklassen zu haben. Hier verwende ich den von Ihnen beschriebenen Ansatz und kombiniere ihn mit einer automatisch generierten Klasse, bei der es sich um ein DTO handelt, das über das Mapping-Plugin übergeben wurde.
Wurzel
8

Bitte schauen Sie sich diese Bibliothek Schreibmaschine an

Es konvertiert nicht nur Klassen, Aufzählungen, Schnittstellen usw., sondern auch die API-Controller, was einfach fantastisch ist.

Außerdem funktioniert dies, sobald Sie die CS-Quelldatei speichern, sodass ich kein externes Tool auslösen muss. Speichern Sie .cs und Sie erhalten .ts aktualisiert

harishr
quelle
Das Tolle ist, dass beim Speichern entsprechender CS-Dateien TS-Dateien erstellt werden. Das Festlegen des Ausgabeordners ist jedoch sehr fragil (siehe Diskussion hier ) und erfordert handgefertigten Code.
Alex Klaus
Ich benutze dafür Build-Tools, und dieses Tool ist viel leistungsfähiger als die meisten hier genannten
Harishr
6

Ich habe eine kleine Lösung, die T4-Vorlagen verwendet ( Quelle anzeigen ).

Sie gehen von jedem CLR POCO:

public class Parent : Person
{
    public string Name { get; set; }
    public bool? IsGoodParent { get; set; }
    public virtual ICollection<Child> Children { get; set; }
}

Zu einer TypeScript-Schnittstelle:

///<reference path="Child.d.ts" />
///<reference path="Person.d.ts" />
interface Parent extends Person {
    Name : string;
    IsGoodParent? : bool;
    Children : Child[];
}
diachedelisch
quelle
3

Sie können das Open-Source-Projekt NSwag verwenden : In der GUI können Sie die .NET-Klasse aus einer vorhandenen .NET-DLL auswählen und die TypeScript-Schnittstelle dafür generieren.

Das Projekt bietet außerdem Befehlszeilentools und Unterstützung für T4-Vorlagen sowie die Generierung von Clientcode für Web-API-Controller ...

Rico Suter
quelle
Beachten Sie, dass NSwag TS-Dateien nur entweder aus den Binärdateien (Sie benötigen auch alle Abhängigkeiten Ihrer Binärdateien) oder aus der JSON-Datei generiert, die Swagger aus den Binärdateien erstellt.
Alex Klaus
3

Wenn Sie für jede generierte TypeScript-Klasse / -Schnittstelle eine separate Datei erstellen müssen (dh in einer "einzelnen Klasse pro Datei"), können Sie TypeGen ausprobieren . Mit diesem Tool können Sie über die Package Manager-Konsole TypeScript-Dateien basierend auf Ihren C # -Klassen / Aufzählungen generieren. Es unterstützt derzeit:

  • Exportieren von Aufzählungen; Exportieren von POCOs als TS-Klassen oder Schnittstellen
  • Erbe
  • generische Typen
  • Sammlung / verschachtelte Sammlungstypen

plus einige zusätzliche Funktionen. Es ist auch Open Source (Sie können es auf Github überprüfen ).

JB1
quelle
2

Wie wäre es umgekehrt?

Schauen Sie sich erecruit TypeScript Translator an . Es wird mit sofort einsatzbereiter C # -Unterstützung geliefert, ist jedoch tatsächlich vorlagenbasiert (verwendet Nunjucks zum Rendern), was bedeutet, dass alles andere generiert werden kann - VB.NET, F #, C ++, XML, SQL - was auch immer Sie mit einer Vorlage codieren können.

Funktioniert als .NET-Konsolenprogramm, NodeJS-Programm (für Benutzer ohne Windows) oder als Visual Studio-Erweiterung mit Funktionen zum Generieren beim Speichern. Und beinhaltet MSBuild-Unterstützung, nur um Ihren Build-Server glücklich zu machen. :-)

Fjodor Soikin
quelle
Ich habe dies getan, weil es wirklich die beste Lösung ist, Generate-on-Save ist das Killer-Feature.
John Zabroski
2

Sie können dies auch verwenden: https://github.com/pankleks/TypeScriptBuilder

Diese kleine Bibliothek generiert eine TypeScript-Typdefinition basierend auf C # -Typen. Verwenden Sie es direkt in Ihrem Backend-C # -Projekt, um Code für Ihr Frontend-TypeScript-Projekt zu generieren. Sie können auch eine kleine Konsolen-App schreiben, um Code mit vorgefertigten Tools zu generieren.

Funktioniert mit dem Full & NET Core Framework!

Installation per Nuget: Install-Package TypeScriptBuilder

Unterstützte Funktionen

  • Typabhängigkeit auflösen
  • Generika
  • Typvererbung
  • Namespaces (Module)
  • Aufzählungen
  • Nullable Typen
  • Wörterbuchkonvertierung (zu starken TS-indizierten Objekten)
  • Satz von Steuerattributen für die Codegenerierung
  • any für Typen, die nicht konvertiert werden können

Weitere Beschreibung: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md

Pankleks
quelle
Gerade ausprobiert TypeScriptBuilderund es sieht bisher ausgezeichnet aus; Hurra für die Unterstützung von .NET Core!
Tobias J
1

Jungs schauen sich das an https://github.com/reinforced/ Reinforced.Typings an . Ich habe in den letzten Tagen mit Typelite- und T4-Vorlagen gespielt und bin zu diesem Projekt gekommen. Es ist super einfach und wirkt wie ein Zauber. Holen Sie sich einfach das Paket, ändern Sie die Konfigurationsdatei (wie für 10 Sekunden) und erstellen Sie. Alles wird automatisch ohne Probleme erledigt. Segne den Autor!

Das Schlechte an T4-Vorlagen ist, dass die gescannten Assemblys nach dem Erstellen aus VS gesperrt sind und Sie VS neu starten müssen (wie dumm ist das?). Es gibt einige Problemumgehungen in T4 Toolbox + einige VS-Reinigungsanweisungen, aber keine davon hat bei mir funktioniert.

veb
quelle
1

Ich mag die Citykid-Lösung. Ich hatte es ein wenig verlängert. Die Lösung basiert also auch auf der Codegenerierungstechnik mit T4-Vorlagen.

Es kann allgemeine TypeScript-Typen und Umgebungsdeklarationen generieren.

Es unterstützt Vererbungs- und Schnittstellenimplementierungen.

Unterstützt Generika, Arrays und Listen als Typfelder.

Außerdem werden TypeScript-Typen übersetzt, die in der Konfiguration nicht explizit erwähnt werden (zum Beispiel importieren wir Typ A, und in der TS-Ausgabe finden Sie einige andere Typen: Feldtypen, Basistypen und Schnittstellen).

Sie können auch den Namen des Typs überschreiben.

Aufzählungen werden ebenfalls unterstützt.

Verwendungsbeispiel (Sie finden es im Projekt-Repository):

// 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" #>

Und Sie erhalten eine Ausgabe

//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
}

Die vollständige Lösung finden Sie hier: https://bitbucket.org/chandrush/cst4ts

Rollender Stein
quelle
1

Sie können auch Bridge.net verwenden. Ab Version 1.7 wird die Generierung von TypeScript-Definitionen für C # -Typen unterstützt. Siehe http://bridge.net/docs/generate-typescript-definitions/

George Birbilis
quelle
Hauptproblem ist, wenn Sie Google-Suche machen, können Sie bei der ursprünglichen Frage landen und nie bemerken, dass nützliche Informationen bei einer doppelten / geschlossenen Antwort vorhanden waren
George Birbilis
Stellen Sie also sicher, dass der Kanoniker die besten Informationen hat. Posten Sie nicht auf mehrere Fragen. Jede weitere Diskussion darüber kann zu Meta Stack Overflow
Martijn Pieters
1

Die Antwort von @ citykid hat mir auch sehr gut gefallen, deshalb habe ich sie erweitert, um jeweils einen ganzen Namespace zu erstellen. Fügen Sie einfach die POCO-Klassen in den Namespace ein und erstellen Sie die T4-Vorlagen neu. Ich wünschte, ich wüsste, wie man separate Dateien für jede generiert, aber das ist nicht das Ende der Welt.

Sie müssen auf die DLL-Dateien im oberen Teil verweisen (wo sich die gewünschten Klassen befinden) und die Namespaces angeben. Alle zu bearbeitenden Zeilen sind mit ACME markiert. Ein großes Lob an @citykid, danke!

<#@ 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
quelle
0

Meine Lösung bestand darin, ein kleines Codegen-Util zu schreiben, das einfach eine Projektassembly (und referenzierende Assemblys) benötigt und mit dem Scannen von Typen beginnt, die an der Interaktion zwischen Typoskript und c # beteiligt sind. Dieses util gibt beide Javascript als d.ts aus ... Das Tool wird im Post-Build-Event aufgerufen ... funktioniert wie ein Zauber!

Paul0515
quelle
Ich weiß nicht, warum ich mich für die Ausgabe von js und d.ts entschieden habe ... im Moment gibt es einfach .ts ... einfach und sehr machbar aus.
Paul0515
1
Hallo Paul ... könnten Sie den Util-Code mit uns teilen? Ich möchte nur eine ts-Datei (an einem bestimmten Ordner) und keine d.ts-Datei erstellen, wie Sie gesagt haben. Kann Ihr Util das?
Krishna Teja Veeramachaneni
0

Bei Interesse können Sie TypedRpc verwenden . Der Zweck besteht nicht nur darin, die Schnittstellen in TypeScript zu erstellen, sondern die gesamte Kommunikation mit dem Dienst in .Net mithilfe des JsonRpc-Protokolls zu erstellen.

Beispiel für eine Klasse im Server:

[TypedRpc.TypedRpcHandler]
public class RpcServerExample
{
    public String HelloWorld()
    {
        return "Hello World!";
    }
}

Verwendung des generierten TypeScript-Codes:

/// <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);

Weitere Beispiele für die Verwendung finden Sie unter https://github.com/Rodris/TypedRpc .

Rodris
quelle
0

ASP.NET Web API-Clientgeneratoren möglicherweise praktischer und haben weniger Aufwand als die Swagger-Toolchain und andere während der SDLC.

Während Programmierer im Allgemeinen WebApiClientGen verwenden, um Client-API-Codes zu generieren, bietet dieses Projekt auch POCO2TS.exe, ein Befehlszeilenprogramm, das TypsScript-Schnittstellen aus POCO-Klassen generiert. Sie können entweder Poco2ts.exe oder die poco2ts-Komponente verwenden, um die Codegenerierung in Ihre Build-Pipeline zu integrieren.

ZZZ
quelle
0

Wenn Sie es über node.js konvertieren möchten, können Sie dieses Paket (csharp-to-typescript) verwenden. https://www.npmjs.com/package/csharp-to-typescript

Quellcode: 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;
}
Sagar Rana Magar
quelle
0

Wenn Sie VSCode verwenden, können Sie sich diese Erweiterung C # für TypeScript ansehen

Konvertieren Sie die C # -Klasse in eine TypeScript-Schnittstelle

Sehen Sie, wie ich es mit nur einem Klick aus C # -Code konvertieren kann. Natürlich werden nicht alle Klassen wie die Factory konvertiert, aber es ist gut genug für die Client-Seite. Wir brauchen nur die Form von Daten. Manchmal, wenn ich das Besuchermuster verwenden muss, werde ich mit zusätzlichen Methoden dekorieren, die ich benötige. Es dauerte nur etwa 5 Sekunden, um dies zu tun. Im Vergleich zu einer Minute oben könnte ich sagen, dass es ein großer Gewinn ist.

Weitere Details finden Sie in meinem Blogbeitrag

trungk18
quelle
0

Ich habe dazu eine Feature-Anfrage auf der Developer Community-Seite geschrieben:

https://developercommunity.visualstudio.com/idea/1153873/reuse-existing-net-classes-for-typescript-definiti.html

Wie Sie in den Antworten hier sehen können, gibt es viele Projekte, die versuchen, dies zu lösen, aber leider sind viele dieser Projekte Einzelentwicklerprojekte, die auf diesem Weg nicht mehr unterstützt werden. Ich denke , es wäre wirklich nett, wenn Microsoft statt eines dieser Projekte erhalten, die Schaffung eines .NETauf dem TypeScriptGenerator.

Einige werden unter anderem noch gewartet, sind jedoch stark von einem einzigen Entwickler abhängig:

TypeLite:

https://bitbucket.org/LukasKabrt/typelite/src/default/

TypeScript-Definitionsgenerator:

https://marketplace.visualstudio.com/items?itemName=MadsKristensen.TypeScriptDefinitionGenerator

Schreibmaschine:

https://github.com/frhagn/Typewriter

TypeGen:

https://github.com/jburzynski/TypeGen

Verstärkte Typisierungen:

https://github.com/reinforced/Reinforced.Typings

Mein Tipp wäre, ein Projekt auszuwählen, von dem Sie von Anfang an erwarten, dass es nicht mehr gewartet wird, und das bereit ist, zu etwas zu wechseln, das derzeit gewartet wird. Ich würde mich für etwas entscheiden, das Anmerkungen verwendet, und die Daumen drücken. Ich könnte einfach dieses Tag durch etwas anderes ersetzen, wenn dieses Projekt nicht mehr unterstützt wird.

Ogglas
quelle