web-dev-qa-db-fra.com

Comment réutiliser des définitions de classe C # existantes dans des projets TypeScript

Je vais juste commencer à utiliser TypeScript dans mon projet client HTML qui appartient à un projet MVC avec un modèle de domaine de structure d'entité déjà présent. Je veux que mes deux projets (côté client et côté serveur) soient totalement séparés, car deux équipes travailleront sur ceci ... JSON et REST sont utilisés pour communiquer des objets dans les deux sens.

Bien entendu, mes objets domain du côté client doivent correspondre aux objets du côté serveur. Dans le passé, je l’avais normalement fait manuellement. Existe-t-il un moyen de réutiliser mes définitions de classe C # (spécialement les classes POJO de mon modèle de domaine) pour créer les classes correspondantes dans TypeScript "? 

101
pabloelustondo

Il n'y a actuellement rien qui mappe C # à TypeScript. Si vous avez beaucoup de POCO ou pensez que ceux-ci pourraient changer souvent, vous pouvez créer un convertisseur - quelque chose de simple qui ressemble à ...

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

À

export class MyPoco {
    public Name: string;
}

Il y a aussi un discussion sur Codeplex sur la génération automatique à partir de C # .

Juste pour garder les choses à jour, TypeLite peut générer des interfaces TypeScript à partir de C #:

_ { http://type.litesolutions.net/

50
Fenton

Web Essentials permet de compiler des fichiers C # en fichiers TypeScript .d.ts lors de l’enregistrement. Ensuite, vous pouvez référencer les définitions à partir de vos fichiers .ts.

enter image description here

32
V.B.

TypeLite et T4TS dessus tous les deux avaient l'air bien, juste choisi un, TypeLite, fourchu pour obtenir de l'aide pour 

  • ValueTypes,
  • Nullables
  • camelCasing _ (le document racine TypeScript utilise des chameaux et cela va trop bien avec C #)
  • champs publics (aime les POCO propres et lisibles, facilite également la tâche du compilateur C #)
  • désactiver la génération de module} _

Ensuite, j’avais besoin de C # interfaces et j’ai pensé qu’il était temps de cuire moi-même et d’écrire un simple script T4 qui ne fait que ce dont j'ai besoin. Il comprend également Enums. Pas de repo nécessaire, juste <100 lignes de T4.

_ {Usage} _
Pas de bibliothèque, pas de NuGet, juste ce simple fichier T4 - utilisez "add item" dans Visual Studio et choisissez n’importe quel modèle T4. Collez ensuite ceci dans le fichier. Adaptez chaque ligne avec "ACME". Pour chaque classe C #, ajoutez une ligne

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

L'ordre importe, tout type connu sera utilisé dans les interfaces suivantes. Si vous utilisez uniquement des interfaces, l'extension de fichier peut être .d.ts. Pour les énumérations, vous avez besoin d'un fichier .ts, car une variable est instanciée.

Personnalisation
Piratez le 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();
    }
#>

Le prochain niveau du script consistera à créer l'interface de service à partir de la classe MVC JsonController.

21
citykid

Voici mon approche pour le résoudre. Déclarez vos classes C # avec un attribut et les fichiers .d.ts seront générés (en utilisant des transformations T4). Il y a un paquet sur nuget et la source est disponible sur github . Je travaille toujours sur le projet, mais le support est assez important.

18
Christoffer

Si vous utilisez Visual Studio, ajoutez l'extension Typewriter.

Galerie Visual Studio

Site Web/Documentation

Mettre à jour

Avec Web Essentials installé dans VS 2015, vous pouvez cliquer avec le bouton droit de la souris sur le fichier de classe, puis sur> Web Essentials> Créer un fichier Intellisense TypeScript dans le menu contextuel.

16
Michael Tranchida

Si vous utilisez vscode, vous pouvez utiliser mon extension csharp2ts qui fait exactement cela.

Il vous suffit de sélectionner le code C # collé et d'exécuter la commande Convert C# to TypeScript à partir de la palette de commandes enter image description here Un exemple de conversion:

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;
}
12
rafael

J'ai créé un petit utilitaire pouvant générer des interfaces TypeScript à partir de classes C #. Est disponible sous forme de package NuGet . Une documentation détaillée est disponible sur la page Web project .

10
Lukas Kabrt

Essayez le cadre Reinforced.Typings. Semble que cela résout votre problème.

  1. Installez-le à partir de NuGet
  2. Accédez à votre POCO et ajoutez l'attribut [TsInterface] au-dessus de celui-ci.

    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. Reconstruisez votre projet
  4. Recherchez le code TypeScript généré dans le fichier %Your_Project_Directory%/Scripts/project.ts et ajoutez-le au projet manuellement

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
    
  5. Faites la même chose pour tous vos POCO et référencez project.ts dans votre autre code TypeScript.

Voir plus de détails dans documentation wiki

10
Pavel B. Novikov

S'il vous plaît jeter un oeil à cette bibliothèque Typewriter

Il convertit non seulement les classes, les enums, les interfaces, etc., mais également les contrôleurs api, ce qui est tout simplement génial.

De plus, cela se produit dès que vous enregistrez le fichier .cs source, ce qui évite de devoir déclencher un outil externe. Enregistrez le .cs et vous obtenez le .ts mis à jour

6
harishr

J'ai une petite solution qui utilise des modèles T4 ( view source ).

Vous passez de n'importe quel POCO CLR:

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

Vers une interface TypeScript:

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

Vous pouvez également utiliser ceci: https://github.com/pankleks/TypeScriptBuilder

Cette petite bibliothèque génère une définition de type TypeScript basée sur les types C # . Utilisez-la directement dans votre projet C # backend pour générer le code de votre projet TypeScript frontend . Vous pouvez également écrire une petite application console pour générer du code par pré-génération outils.

Fonctionne sur Full & NET Core framework!

Installer par nuget: Install-Package TypeScriptBuilder

Fonctions prises en charge

  • Résolution de dépendance de type
  • Génériques
  • Type héritage
  • Espaces de noms (modules)
  • Enums
  • Types nullables
  • Conversion de dictionnaire (en objets forts indexés TS)
  • Ensemble d'attributs de contrôle de génération de code
  • any pour les types qui ne peuvent pas être convertis

Plus de description: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md

3
user1038334

Vous pouvez utiliser le projet open-source NSwag : dans l'interface graphique, vous pouvez sélectionner une classe .NET à partir d'un .NET DLL existant et générer l'interface TypeScript correspondante. 

Le projet fournit également des outils de ligne de commande et une prise en charge des modèles T4, ainsi que la génération de code client pour les contrôleurs Web API ...

3
Rico Suter

Que diriez-vous de l'inverse? 

Découvrez erecruit TypeScript Translator . Il est livré avec un support C # prêt à l'emploi, mais est en fait basé sur des modèles (utilise Nunjucks pour le rendu), ce qui signifie qu'il peut générer autre chose - VB.NET, F #, C++, XML, SQL - tout ce que vous pouvez encoder avec un modèle.

Fonctionne en tant que programme console .NET, programme NodeJS (pour ceux qui ne travaillent pas sous Windows) ou en tant qu'extension Visual Studio , complète avec la fonctionnalité de génération sur enregistrement. Et inclut le support MSBuild, juste pour rendre votre serveur de construction heureux. :-)

2
Fyodor Soikin

Les gars regardent https://github.com/reinforced/Reinforced.Typings. Je joue avec les templates Typelite et T4 depuis quelques jours et je me suis retrouvé avec ce projet. C'est super simple et fonctionne comme un charme. Obtenez juste le paquet, modifiez le fichier de configuration (c'est comme pendant 10 secondes) et construisez. Tout se fait automatiquement sans aucun problème. Bénis l'auteur!

La mauvaise chose à propos des modèles T4 est qu’une fois que vous construisez à partir de VS, les assemblages analysés sont verrouillés et que vous devez redémarrer VS (qu’est-ce que c’est idiot?). Il existe certaines solutions de contournement dans T4 Toolbox + certaines directives de nettoyage de VS, mais aucune de celles-ci ne fonctionnait pour moi.

1
veb

J'ai aussi beaucoup aimé la réponse de @ citykid, je l'ai donc étendue pour créer un espace de noms complet à la fois. Il suffit de placer les classes POCO dans l'espace de noms et de reconstruire les modèles T4. J'aimerais bien savoir comment générer des fichiers séparés pour chacun, mais pas pour la fin du monde. 

Vous devez référencer les fichiers .DLL dans la partie supérieure (où se trouvent les classes que vous voulez) et mentionner les espaces de noms. Toutes les lignes à éditer sont marquées ACME. Major Cudos à @citykid, appréciez-le!

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

J'aime la solution citykid. Je devais étendre un peu .. .. Donc, la solution est également basée sur la technique de génération de code avec des modèles T4.

Il peut générer des types TypeScript et des déclarations ambiantes courants.

Il supporte les implémentations d'héritage et d'interface.

Prend en charge les génériques, les tableaux et les listes en tant que champs types.

Cela se traduit également par des types TypeScript qui ne sont pas explicitement mentionnés dans la configuration (par exemple, nous importons le type A et dans la sortie TS, vous pouvez trouver d'autres types: types de champs, types de base et interfaces).

Vous pouvez également remplacer le nom du type.

Les énumérations sont également supportées.

Exemple d'utilisation (vous pouvez le trouver dans le référentiel du projet):

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

Et vous obtiendrez une sortie

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

Vérifiez la solution complète ici: https://bitbucket.org/chandrush/cst4ts

1
RollingStone

Si quelqu'un souhaite une solution Web, j'ai créé un site Web simple qui convertira les classes C # DTO en interfaces TypeScript. http://dto2ts.ga/ (Ce n'est pas encore gratuit, mais)

0
karoluS

Si cela vous intéresse, vous pouvez utiliser TypedRpc . Son but n'est pas seulement de créer les interfaces dans TypeScript, mais de créer toutes les communications avec le service dans .Net en utilisant le protocole JsonRpc.

Exemple pour une classe sur le serveur:

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

Utilisation du code TypeScript généré:

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

Consultez https://github.com/Rodris/TypedRpc pour d'autres exemples d'utilisation.

0
Rodris

Ma solution a été d'écrire un petit utilitaire codegen qui prend simplement un assemblage de projet (et des assemblys référents) et de commencer à analyser les types impliqués dans l'interaction entre TypeScript et c #. Cet utilitaire génère à la fois javascript et d.ts ... L'outil est appelé dans l'événement post-build ... fonctionne à merveille!

0
Paul0515

Si vous souhaitez le convertir via node.js, vous pouvez utiliser ce package (csharp-to-TypeScript). https://www.npmjs.com/package/csharp-to-TypeScript

code source: 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
Sagar Rana Magar

Vous pouvez également utiliser Bridge.net. Depuis la version 1.7, il prend en charge la génération de définitions TypeScript Pour les types C #. Voir http://bridge.net/docs/generate-TypeScript-definitions/

0
George Birbilis

Générateurs de clients API Web ASP.NET peuvent être plus pratiques et moins onéreux que swagger toolchain et d’autres pendant SDLC.

Alors que les programmeurs utilisent généralement WebApiClientGen pour générer des codes API client, ce projet fournit également POCO2TS.exe, un programme de ligne de commande qui génère des interfaces TypsScript à partir de classes POCO. Vous pouvez utiliser Poco2ts.exe ou le composant poco2ts pour intégrer la génération de code à votre pipeline de construction.

0
ZZZ

Si vous devez créer un fichier séparé pour chaque classe/interface TypeScript générée (c'est-à-dire de manière "classe unique par fichier"), vous pouvez essayer TypeGen . C'est un outil que vous pouvez utiliser à partir de la console du gestionnaire de packages pour générer des fichiers TypeScript basés sur vos classes/énumérations C #. Il supporte actuellement:

  • exportation d'énumérations; exportation de POCO en tant que classes ou interfaces TS
  • héritage
  • types génériques
  • collection/types de collection imbriqués

plus quelques fonctionnalités supplémentaires. Il est également open source (vous pouvez le vérifier sur github ).

0
JB1