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


121

Je vais juste commencer à l'utiliser TypeScriptdans mon projet client HTML qui appartient à un projet MVC avec un modèle de domaine de cadre 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 là-dessus ... JSON et REST sont utilisés pour communiquer des objets dans les deux sens.

Bien sûr, mes domainobjets côté client doivent correspondre aux objets côté serveur. Dans le passé, je l'ai normalement fait manuellement. Existe-t-il un moyen de réutiliser mes définitions de classe C # (en particulier les POJOclasses de mon modèle de domaine) pour créer les classes correspondantes dans TypeScript "?


Non, ce sont des langues totalement différentes.
Hans Passant

6
Oui, je voulais dire POCO. Je sais que ce sont des langages totalement différents ... mais ce serait bien d'éviter de taper à nouveau tout ça ... je pense que pour le moment je vais copier et coller mon code c # .. et refactoriser tout ce qu'il compile en TypeScript ... de cette façon, je suis sûr que je n'ai pas tapé un attribut erroné .. et m'aide à ne pas avoir à m'en souvenir. Mais cela ne sonne clairement pas du tout.
pabloelustondo

1
Je suis également intéressé par l'autre direction - des classes de modèle Typescript aux classes de modèle C #, désérialisable de manière bidirectionnelle via JSON.
Jason Kleban

Je cherchais le même et je viens de tomber sur ce plugin Gulp bien que je ne sache pas à quel point il est supporté
Luke T O'Brien

Réponses:


54

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

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

À

export class MyPoco {
    public Name: string;
}

Il y a aussi une 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/


Ouaisp, quelque chose comme ça est ce que je pensais faire manuellement .. bien. Je ne suis pas sûr d'avoir le temps d'écrire un convertisseur maintenant .. mais oui, ce serait l'idée. Merci. Peut-être que Microsoft est en train de le faire maintenant pendant que nous parlons.
pabloelustondo

Vous pouvez le faire de deux manières ... Générez des fichiers .ts avec l'aide d'EnvDTE Automation ou en utilisant le catalogue d'annuaires Mef et Reflection / Introspection. Je préfère le second car il ne dépend pas du visuel.
Arek Bal

1
Je commençais à utiliser dart et je suis tombé sur le même problème de partage de types entre c # et javascript. J'espérais que ce problème serait résolu par Typescript, mais il semble que la réponse ne soit pas encore. J'ai aussi été un peu gâté par le compilateur saltarelle qui compilait plutôt bien c # en javascript saltarelle-compiler.com
BraveNewMath

2
@DanielHarvey Les énumérations sont générées correctement par TypeLite, probablement corrigées depuis votre commentaire.
pauloya

1
TypeScriptPaste convertira C # en Typescript. Vous devez avoir le compilateur Roslyn, ce qui signifie Visual Studio 2015, mais cela fonctionne très bien. Ce n'est pas à 100% et j'ai dû abattre un peu de code - convertir foreach en boucles for, écrire ma propre classe List <T>, par exemple, et vous devez structurer votre code pour qu'il soit `` pur '' avec uniquement des structures de données internes pour travailler avec, mais c'était un petit prix à payer pour pouvoir modifier et tester le code dans VS, puis le «compiler» en Javascript via TypeScript.
John Mott

36

Web Essentials permet de compiler des fichiers C # en fichiers TypeScript .d.tslors de l'enregistrement. Ensuite, vous pouvez référencer les définitions de vos .tsfichiers.

entrez la description de l'image ici


Êtes-vous peut-être en train de confondre les fichiers .ts et c #? impossible de trouver cette option
Flores

1
Mais après quelques recherches, j'ai trouvé ce que tu veux dire ... en fait très utile.
Flores

2
En fait, ce n'est pas le cas, je suis sûr que l'opération ne «compile» rien, elle génère du code. Cela m'a dérouté, mais ce n'est pas correct à 99%, je vais vous le donner ;-)
Flores

1
C'était plus utile si nous pouvions configurer son espace de noms et quelques personnalisations.
Farshid Saberi

5
Vous devez maintenant installer manuellement 'TypeScript Definition Generator' pour l'utiliser - voir ici
Taran

24

TypeLite et T4TS ci-dessus avaient l'air bien, je n'en ai choisi qu'un, TypeLite, l'a fourchu pour obtenir du soutien pour

  • ValueTypes ,
  • Nullables
  • camelCasing (la documentation racine de TypeScript utilise des chameaux, et cela va trop bien avec C #)
  • champs publics (adore 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 d' interfaces C # et j'ai pensé qu'il était temps de créer mon propre truc et j'ai écrit un simple script T4 qui fait exactement ce dont j'avais besoin. Il comprend également Enums . Aucun repo requis, juste <100 lignes de T4.

Utilisation
Pas de bibliothèque, pas de NuGet, juste ce simple fichier T4 - utilisez "ajouter un élément" dans Visual Studio et choisissez n'importe quel modèle T4. Puis collez-le dans le fichier. Adaptez chaque ligne avec "ACME" dedans. Pour chaque classe C #, ajoutez une ligne

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

L'ordre est important, tout type connu sera utilisé dans les interfaces suivantes. Si vous n'utilisez que 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
Hackez 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 niveau suivant du script consistera à créer l'interface de service à partir de la classe MVC JsonController.


1
Très mignon, mais vous avez typeof(ParticleKind)dans votre Enum<T>. Cela devrait sûrement être typeof(T)? Vous devez également mettre à jour boolpour être booleanpour les versions ultérieures de TypeScript
Gone Coding

Remarquez également: cela rompt avec les propriétés d'énumération dans les classes
Codage

1. Euh, oui, merci pour la mise en garde, va régler ça. 2. Le typographie a évolué depuis l'écriture de ce script. On dirait qu'il pourrait avoir besoin d'une mise à jour, vous avez certainement raison. J'espère que cela servira de point de départ pour votre propre objectif.
citykid

1
Je l'ai converti en une bibliothèque d'aide, corrigé les bogues et n'utilise maintenant que des entrées sur une seule ligne dans mes fichiers TT pour générer des interfaces et les nouveaux const enums. Merci pour l'exemple. C'était très proche et j'ai obtenu un résultat de travail beaucoup plus rapidement.
Fini le codage le

23

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 Convert C# to TypeScriptcommande à partir de la palette de commandes entrez la description de l'image ici 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;
}

1
Je pense que les gens sont intéressés par une solution entièrement automatisée où la compilation avec msbuild génère les définitions dactylographiées avant de les consommer. Y a-t-il un moyen d'obtenir cela en utilisant le plugin?
binki

Non, c'est un plugin VSCode, ne fonctionne que dans l'éditeur et non comme un programme autonome
Rafael

J'ai converti cette extension vscode en module de nœud si vous le souhaitez via node.js ou tytpescript. Vous pouvez vérifier le repo ici: github.com/YuvrajSagarRana/csharp-to-typescript
Sagar Rana Magar

Existe-t-il un moyen de lier la commande au raccourci clavier? Par exemple, sélectionnerait le code .net, et avoir setup shift + ctrl + c pour exécuter "Convert C # to TypeScript", cela accélérerait le processus. J'aime le plugin!
Pawel Cioch


18

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 sur le fichier de classe, puis> Web Essentials> Créer un fichier Intellisense Typescript dans le menu contextuel.


Il a créé le fichier d.ts pour le support intellisense, mais comment l'utiliser et quelle est la relation avec la création des classes sous-jacentes? Je suis allé sur les WebEssentails et je ne trouve aucun document. Aucune suggestion? TIA.
howardlo

@hio Faites simplement référence au fichier d.ts dans le fichier .ts dans lequel vous avez besoin de l'utiliser. Lorsque / si vous changez de classe, le fichier d.ts sous-jacent sera mis à jour. (Astuce: vous pouvez faire glisser et déposer le fichier d.ts en haut d'un fichier .ts et il créera la référence pour vous.)
Michael Tranchida

Merci Michael! J'ai essayé de déposer le fichier d.ts dans un fichier .ts vide et il n'a ajouté que le fichier au dossier contenant. Je l'ai essayé dans VS2015 et le nouveau VS2017, mêmes résultats. Connaissez-vous une vidéo ou un didacticiel qui montre comment tout cela fonctionne? TIA
howardlo

J'ai essayé de déposer le fichier d.ts dans un fichier .ts dans l'éditeur et il a créé la référence. Je pense que je comprends maintenant. Tutoriel toujours bienvenu. Merci!
howardlo

@hlo Désolé, je ne connais aucun tutoriel. Lorsque je vais sur la route Web Essentials, j'ai créé des fichiers .ts en double parce que mes modèles de domaine sont dans une bibliothèque de classes, et dans mon code côté client, j'ai généralement besoin de créer les modèles dto ou vm, dont le navigateur a besoin savoir a propos. Si vous rencontrez des problèmes avec tout cela, essayez l'extension Typewriter. Il vous permet d'avoir vos fichiers de modèle .ts où vous le souhaitez.
Michael Tranchida

15

Essayez le framework Reinforced.Typings. Il semble que cela résout votre problème.

  1. Installez-le depuis NuGet
  2. Accédez à votre POCO et ajoutez un [TsInterface]attribut au-dessus

    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. Découvrez le code TypeScript généré dans le %Your_Project_Directory%/Scripts/project.tsfichier et ajoutez-le manuellement au projet

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

Voir plus de détails dans wiki de la documentation


Ajoutez l'extrait de code plutôt que de ne partager que l'URL
Venkat.R

Il y a wiki avec quickstart O_o github.com/reinforced/Reinforced.Typings/wiki
Pavel

Mais ok, je vais donner un exemple ici :)
Pavel B.Novikov

Pas de support .NET Core ( lien ) :(
Alex Klaus

5
@AlexKlaus .NET Core support est arrivé
Pavel

10

J'ai créé un petit utilitaire qui peut 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 du projet .


Belle bibliothèque, bon effort. Malheureusement, je n'ai pas trouvé de moyen de modifier le type de valeur produit, par exemple convertir "chaîne" en "KnockoutObservableString" ou "chaîne []" en "KnockoutObservableArray". Est-ce quelque chose qui figure dans les plans pour un avenir proche?
racine

2
J'ai utilisé T4TS à la place, cela a pris 14 minutes.
root

@root, vous ne devriez pas réellement convertir une chaîne [] en KnockoutObservableArray dans les définitions générées. Vous utilisez ces définitions générées lorsque vous utilisez réellement les instances sérialisées de ces classes. Maintenant, vous exécutez peut-être l'objet entier via le plugin de mappage, mais je vous mets en garde contre cela. Cela peut être excessif et je préfère toujours convertir manuellement (probablement via un ctor) vos instances sérialisées de classes C # en classes TypeScript pleines d'observables. Le seul moment où je suggérerais de l'exécuter à l'aveuglette via le plugin de cartographie est pour une application de style CRUD.
Allen Rice

@Lukas Kabrt, merci beaucoup pour TypeLite, cela a été extrêmement bénéfique dans nos projets et j'en ai brièvement parlé ici: underwatergorilladome.com/...
Allen Rice

@AllenRice, j'ai trouvé pratique d'avoir des classes de modèle de vue Knockout fabriquées à la main, c'est là que j'utilise l'approche que vous avez décrite et que je la combine avec une classe générée automatiquement qui est un DTO qui a été transmis via le plugin de mappage.
root

7

Veuillez jeter un œil à cette bibliothèque Machine à écrire

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

De plus, il le fait dès que vous enregistrez le fichier .cs source, donc je n'ai pas à déclencher d'outil externe. Enregistrez .cs et vous obtenez .ts mis à jour


L'avantage est qu'il produit des fichiers TS lors de la sauvegarde des fichiers CS correspondants. Cependant, la configuration du dossier de sortie est assez fragile (voir la discussion ici ) et nécessite du code fabriqué à la main.
Alex Klaus

J'utilise des outils de construction pour cela, et cet outil est bien plus puissant que la plupart des mentionnés ici
harishr

6

J'ai une petite solution qui utilise des modèles T4 ( voir la 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[];
}

3

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

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


Sachez que NSwag générera des fichiers TS uniquement à partir des fichiers binaires (vous avez également besoin de toutes les dépendances de vos binaires) ou de son fichier JSON, que Swagger produit à partir des fichiers binaires.
Alex Klaus

3

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

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

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


2

Et l'inverse?

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

Fonctionne comme un programme de console .NET, un programme NodeJS (pour ceux qui ne sont pas sous Windows) ou comme une extension Visual Studio , avec une fonctionnalité de génération lors de l'enregistrement. Et inclut le support MSBuild, juste pour rendre votre serveur de build heureux. :-)


J'ai attribué un +1 parce que c'est vraiment la meilleure solution, la génération sur sauvegarde étant la fonctionnalité qui tue.
John Zabroski

2

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-le directement dans votre projet C # backend pour générer du code pour votre projet TypeScript frontend. Vous pouvez également créer une petite application console pour générer du code à l'aide d'outils de pré-construction.

Fonctionne sur le framework Full & NET Core!

Installer par nuget: Install-Package TypeScriptBuilder

Fonctionnalités prises en charge

  • Résolution de la dépendance de type
  • Génériques
  • Héritage de type
  • Espaces de noms (modules)
  • Enums
  • Types nulles
  • Conversion de dictionnaire (vers des objets indexés TS de type fort)
  • 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


Je viens d'essayer TypeScriptBuilderet ça a l'air excellent jusqu'à présent; Hourra pour le support .NET Core!
Tobias J

1

Les gars jettent un œil sur https://github.com/reinforced/ Reinforced.Typings . J'ai joué avec les modèles typelite et t4 ces derniers jours et j'ai fini avec ce projet. C'est super simple et fonctionne comme un charme. Il suffit de récupérer le paquet, de modifier le fichier de configuration (c'est comme 10 secondes) et de construire. Tout se fait automatiquement sans aucun problème. Bénissez l'auteur!

La mauvaise chose à propos des modèles T4 est qu'une fois que vous construisez à partir de VS, les assemblys analysés sont verrouillés et vous devez redémarrer VS (à quel point est-ce idiot?). Il existe une solution de contournement dans T4 Toolbox + quelques directives de nettoyage VS, mais aucune de celles-ci n'a fonctionné pour moi.


1

J'aime la solution citykid. Je devais le prolonger un peu. Ainsi, 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 courants et des déclarations ambiantes.

Il prend en charge les implémentations d'héritage et d'interface.

Prend en charge les génériques, les tableaux et les listes comme champs de type.

De plus, cela se traduit 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 prises en charge.

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

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/


Le problème principal est que lorsque vous effectuez une recherche sur Google, vous pouvez atterrir à la question d'origine et ne jamais remarquer que des informations utiles existaient à une réponse en double / fermée
George Birbilis

Assurez-vous donc que le canonique contient les meilleures informations. Ne postez pas sur plusieurs questions. Toute discussion supplémentaire à ce sujet peut être prise sur Meta Stack Overflow
Martijn Pieters

1

J'ai aussi beaucoup aimé la réponse de @ citykid, alors je l'ai étendue pour faire un espace de noms entier à la fois. Mettez simplement les classes POCO dans l'espace de noms et reconstruisez les modèles T4. J'aimerais savoir comment générer des fichiers séparés pour chacun, mais ce n'est pas la fin du monde.

Vous devez référencer les fichiers .DLL dans la partie supérieure (où se trouvent les classes que vous souhaitez) et vous devez mentionner les espaces de noms. Toutes les lignes à modifier sont marquées avec ACME. Grands félicitations à @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();
    }
#>

0

Ma solution était d'écrire un petit utilitaire de codegen qui prend simplement un assemblage de projet (et référençant des assemblys) et de commencer à analyser les types impliqués dans l'interaction entre typescript et c #. Cet util produit les deux javascript en tant que d.ts ... L'outil est appelé dans l'événement post-build ... fonctionne comme un charme!


Je ne sais pas pourquoi j'ai choisi de sortir js et d.ts ... pour le moment, il produit simplement .ts ... simple et très faisable.
Paul0515

1
Salut Paul ... pourriez-vous partager le code util avec nous. Je cherche simplement à créer un fichier ts (à un certain emplacement de dossier) et non un fichier d.ts comme vous l'avez dit. Est-ce quelque chose que votre util peut faire?
Krishna Teja Veeramachaneni

0

Si vous êtes intéressé, 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 dans 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 de son utilisation.


0

Générateurs de client API Web ASP.NET peuvent être plus pratiques, moins de frais généraux que la chaîne d'outils swagger et autres pendant le 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 build.


0

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

Si vous utilisez VSCode, vous pouvez jeter un œil à cette extension C # en TypeScript

Convertir la classe C # en interface TypeScript

Voyez comment je peux le convertir à partir du code C # en un seul clic. Bien sûr, toutes les classes ne seront pas converties comme l'usine, mais c'est assez bon pour le côté client. Nous avons juste besoin de la forme des données. Parfois, si j'ai besoin d'utiliser le modèle de visiteur, je vais décorer avec des méthodes supplémentaires dont j'ai besoin. Cela n'a pris que 5 secondes pour le faire. En comparant avec une minute ci-dessus, je pourrais dire que c'est une grande victoire.

Voir plus de détails dans mon article de blog

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.