Cómo reutilizar las definiciones de clases de C # existentes en proyectos de TypeScript

121

Voy a comenzar a usarlo TypeScripten mi proyecto de cliente HTML que pertenece a un proyecto MVC con un modelo de dominio de marco de entidad que ya está allí. Quiero que mis dos proyectos (del lado del cliente y del lado del servidor) estén totalmente separados ya que dos equipos trabajarán en esto ... JSON y REST se utilizan para comunicar objetos de un lado a otro.

Por supuesto, mis domainobjetos en el lado del cliente deben coincidir con los objetos en el lado del servidor. En el pasado, normalmente lo hacía manualmente. ¿Hay alguna manera de reutilizar mis definiciones de clase C # (especialmente de las POJOclases en mi modelo de dominio) para crear las clases correspondientes en TypeScript "?

pabloelustondo
fuente
No, son idiomas totalmente diferentes.
Hans Passant
6
Sí, me refiero a POCO. Sé que son lenguajes totalmente diferentes ... pero sería genial evitar de alguna manera volver a escribir todo esto ... Creo que por el momento copiaré y pegaré mi código c # ... y lo refactorizaré hasta que se compile en TypeScript ... de esa manera estoy seguro de que no escribí un atributo incorrecto ... y me ayuda a no tener que recordar. Pero esto claramente no suena nada agradable.
pabloelustondo
1
También estoy interesado en la otra dirección: desde las clases de modelo de Typecript hasta las clases de modelo de C #, que se pueden deserializar bidireccionalmente a través de JSON.
Jason Kleban
He estado buscando lo mismo y acabo de encontrar este complemento de Gulp, aunque no sé cuánto soporte hay para él
Luke T O'Brien

Respuestas:

54

Actualmente no hay nada que asigne C # a TypeScript. Si tiene muchos POCO o cree que pueden cambiar con frecuencia, puede crear un convertidor, algo simple en la línea de ...

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

A

export class MyPoco {
    public Name: string;
}

También hay una discusión sobre Codeplex sobre la generación automática desde C # .

Solo para mantener las cosas actualizadas, TypeLite puede generar interfaces TypeScript desde C #:

http://type.litesolutions.net/

Fenton
fuente
Sí, algo como esto es lo que estaba pensando en hacer manualmente ... bien. No estoy seguro de tener tiempo para escribir un convertidor ahora ... pero sí, esa sería la idea. Gracias. Puede que Microsoft lo esté haciendo ahora mismo mientras hablamos.
pabloelustondo
Puede hacer esto de dos maneras ... Genere archivos .ts con la ayuda de EnvDTE Automation o usando Mef Directory Catalog y Reflection / Introspection. Prefiero el segundo porque no depende de lo visual.
Arek Bal
1
Estaba empezando a usar dart y me encontré con el mismo problema de cómo compartir tipos entre c # y javascript. Esperaba que Typecript resolviera este problema, pero parece que la respuesta aún no está. También me echó un poco el compilador de saltarelle que compiló c # en javascript bastante bien saltarelle-compiler.com
BraveNewMath
2
@DanielHarvey Las enumeraciones son generadas correctamente por TypeLite, probablemente corregidas desde su comentario.
pauloya
1
TypeScriptPaste convertirá C # en Typescript. Tienes que tener el compilador Roslyn, lo que significa Visual Studio 2015, pero funciona muy bien. No es 100% y tuve que simplificar un poco de código: convertir foreach en bucles for, escribir mi propia clase List <T>, por ejemplo, y tienes que estructurar tu código para que sea 'puro' con solo estructuras de datos internas para trabajar con, pero fue un pequeño precio a pagar por poder modificar y probar el código en VS y luego 'compilarlo' en Javascript a través de TypeScript.
John Mott
36

Web Essentials permite compilar archivos C # en archivos TypeScript .d.tsal guardar. Luego, podría hacer referencia a las definiciones de sus .tsarchivos.

ingrese la descripción de la imagen aquí

VB
fuente
¿Quizás está confundiendo los archivos .ts y c #? no puedo encontrar esta opción
Flores
1
Pero después de buscar un poco, encontré lo que quieres decir ... en realidad bastante útil.
Flores
2
En realidad no lo es, estoy bastante seguro de que la operación 'compila' nada, genera código. Eso me confundió, pero no es 99% correcto, te lo doy ;-)
Flores
1
Sería más útil si pudiéramos configurar su espacio de nombres y alguna personalización.
Farshid Saberi
5
Ahora tiene que instalar 'Generador de definiciones de TypeScript' manualmente para usar esto - vea aquí
Taran
24

TypeLite y T4TS de arriba se veían bien, solo elegí uno, TypeLite, lo bifurqué para obtener soporte para

  • ValueTypes ,
  • Nulables
  • camelCasing (el documento raíz de TypeScript usa camellos, y esto va demasiado bien junto con C #)
  • campos públicos (me encantan los POCO limpios y legibles, también lo hace fácil para el compilador de C #)
  • deshabilitar la generación de módulos

Luego necesitaba interfaces C # y pensé que era hora de hacer lo mío y escribí un script T4 simple que solo hace lo que necesito. También incluye Enums . No se requiere repositorio, solo <100 líneas de T4.

Uso
Sin biblioteca, sin NuGet, solo este simple archivo T4 - use "agregar elemento" en Visual Studio y elija cualquier plantilla T4. Luego péguelo en el archivo. Adapte cada línea con "ACME" en ella. Para cada clase de C # agregue una línea

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

El orden importa, se utilizará cualquier tipo conocido en las siguientes interfaces. Si usa solo interfaces, la extensión del archivo puede ser .d.ts , para las enumeraciones necesita un archivo .ts , ya que se crea una instancia de una variable.

Personalización
Hackea el guión.

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

El siguiente nivel del script será crear la interfaz de servicio a partir de la clase MVC JsonController.

ciudad
fuente
1
Muy lindo, pero lo tienes typeof(ParticleKind)en tu Enum<T>. ¿Seguro que debería ser así typeof(T)? También debe actualizar boolpara booleanversiones posteriores de TypeScript
Gone Coding
También tenga en cuenta: esto rompe con las propiedades de enumeración en las clases
Gone Coding
1. Uhh, sí, gracias por el aviso, lo arreglaré. 2. Mecanografiado evolucionó desde que se escribió este guión. Parece que podría necesitar alguna actualización, ciertamente tienes razón. Espero que sirva como punto de partida para su propio propósito.
citykid
1
Lo convertí en una biblioteca auxiliar, solucioné los errores y ahora uso solo entradas de una sola línea en mis archivos TT para generar interfaces y los nuevos const enums. Gracias por el ejemplo. Estuvo muy cerca y obtuve un resultado de trabajo mucho más rápido.
Gone Coding
23

Si usa vscode, puede usar mi extensión csharp2ts que hace exactamente eso.

Simplemente seleccione el código C # pegado y ejecute el Convert C# to TypeScriptcomando desde la paleta de comandos ingrese la descripción de la imagen aquí Un ejemplo de conversión:

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

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

a

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

    /**Person name */
    Name : string;
}
Rafael
fuente
1
Creo que la gente está interesada en una solución totalmente automatizada donde la compilación con msbuild genera las definiciones de mecanografiado antes de consumirlas. ¿Hay alguna manera de conseguirlo usando el complemento?
binki
No, este es un complemento de VSCode, solo funciona dentro del editor y no como un programa independiente
Rafael
He convertido esta extensión de vscode en un módulo de nodo si lo desea a través de node.js o tytpescript. Puede consultar el repositorio aquí: github.com/YuvrajSagarRana/csharp-to-typescript
Sagar Rana Magar
¿Hay alguna forma de vincular el comando al método abreviado de teclado? Por ejemplo, seleccionaría el código .net y tener la configuración shift + ctrl + c para ejecutar "Convertir C # a TypeScript", eso aceleraría el proceso. ¡Me gusta el complemento!
Pawel Cioch
18

Este es mi enfoque para resolverlo. Declare sus clases de C # con un atributo y se generarán archivos .d.ts (usando transformaciones T4). Hay un paquete en nuget y la fuente está disponible en github . Todavía estoy trabajando en el proyecto, pero el soporte es bastante extenso.

Christoffer
fuente
Bifurqué este proyecto y agregué soporte knockout aquí: github.com/omniscient/t4ts
Frank.Germain
18

Si usa Visual Studio, agregue la extensión Typewriter.

Galería de Visual Studio

Sitio web / documentación

Actualizar

Con Web Essentials instalado en VS 2015, puede hacer clic con el botón derecho en el archivo de clase, luego> Web Essentials> Crear archivo Typecript Intellisense en el menú contextual.

Michael Tranchida
fuente
Creó el archivo d.ts para compatibilidad con intellisense, pero ¿cómo se usa y luego cuál es la relación con la creación de las clases subyacentes? Fui a WebEssentails y no puedo encontrar ningún documento. ¿Alguna sugerencia? TIA.
howardlo
@hio Simplemente haga referencia al archivo d.ts en cualquier archivo .ts en el que necesite usarlo. Cuando / si cambia su clase, el archivo d.ts subyacente se actualizará. (Sugerencia: puede arrastrar y soltar el archivo d.ts en la parte superior de un archivo .ts y creará la referencia para usted.)
Michael Tranchida
Gracias Michael! Intenté colocar el archivo d.ts en un archivo .ts vacío y solo agregué el archivo a la carpeta que lo contenía. Lo probé en VS2015 y el nuevo VS2017, mismos resultados. ¿Conoce algún video o tutorial que muestre cómo funciona todo esto? TIA
howardlo
Intenté colocar el archivo d.ts en un archivo .ts en el editor y creó la referencia. Creo que ahora lo entiendo. El tutorial aún es bienvenido. ¡Gracias!
howardlo
@hlo Lo siento, no conozco ningún tutorial. Cuando sigo la ruta de Web Essentials, he estado creando archivos .ts duplicados porque mis modelos de dominio están en una biblioteca de clases, y en mi código del lado del cliente, generalmente necesito crear los modelos dto o vm, que el navegador necesita saber sobre. Si tiene problemas con todo esto, pruebe la extensión Typewriter. Le permite tener sus archivos de modelo .ts en cualquier lugar que desee.
Michael Tranchida
15

Prueba el marco de Reinforcement.Typings. Parece que resuelve tu problema.

  1. Instalarlo desde NuGet
  2. Navegue a su POCO y agregue el [TsInterface]atributo encima de él

    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. Reconstruye tu proyecto
  4. Descubra el código TypeScript generado en el %Your_Project_Directory%/Scripts/project.tsarchivo y agréguelo al proyecto manualmente

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
  5. Haga lo mismo para todos sus POCO y haga referencia project.tsen su otro código TypeScript.

Ver más detalles en la wiki de documentación

Pavel B. Novikov
fuente
Agregue el fragmento de muestra en lugar de compartir solo la URL
Venkat.R
Hay wiki con inicio rápido O_o github.com/reinforcement/Reinforced.Typings/wiki
Pavel B. Novikov
Pero está bien, daré un ejemplo aquí :)
Pavel B. Novikov
Sin compatibilidad con .NET Core ( enlace ) :(
Alex Klaus
5
Ha llegado el soporte de @AlexKlaus .NET Core
Pavel B. Novikov
10

He creado una pequeña utilidad que puede generar interfaces TypeScript a partir de clases C #. Está disponible como paquete NuGet . La documentación detallada se puede encontrar en la página web del proyecto .

Lukas Kabrt
fuente
Buena biblioteca, buen esfuerzo. Desafortunadamente, no encontré una manera de modificar el tipo de valor producido, por ejemplo, convertir "cadena" a "KnockoutObservableString" o "cadena []" a "KnockoutObservableArray". ¿Es algo que está en planes de futuro cercano?
root
2
En su lugar, usé T4TS, me tomó 14 minutos.
root
@root, en realidad no debería convertir una cadena [] a KnockoutObservableArray en las definiciones generadas. Utiliza estas definiciones generadas cuando en realidad consume las instancias serializadas de estas clases. Ahora, es posible que esté ejecutando todo el objeto a través del complemento de mapeo, pero le advierto contra esto. Eso puede ser excesivo y siempre prefiero convertir manualmente (probablemente a través de un ctor) de sus instancias serializadas de clases C # a clases de TypeScript llenas de observables. La única vez que sugeriría ejecutarlo ciegamente a través del complemento de mapeo es para una aplicación de estilo CRUD.
Allen Rice
@Lukas Kabrt, muchas gracias por TypeLite, ha sido extremadamente beneficioso en nuestros proyectos y lo escribí brevemente aquí: submarinogorilladome.com/…
Allen Rice
@AllenRice, encontré útil tener clases de modelo de vista Knockout hechas a mano, que es donde utilizo el enfoque que describiste y lo combino con una clase generada automáticamente que es un DTO que se pasó a través del complemento de mapeo.
root
7

Por favor, eche un vistazo a esta biblioteca. Máquina de escribir

Convierte no solo clases, enumeraciones, interfaces, etc., sino también los controladores api, lo cual es simplemente increíble.

Además, hace esto tan pronto como guarda el archivo .cs de origen, por lo que no tengo que activar ninguna herramienta externa. Guarde .cs y obtendrá .ts actualizados

harishr
fuente
Lo mejor es que produce archivos TS al guardar los archivos CS correspondientes. Sin embargo, configurar la carpeta de salida es bastante frágil (vea la discusión aquí ) y requiere un código hecho a mano.
Alex Klaus
Utilizo herramientas de compilación para eso, y esta herramienta es mucho más poderosa que la mayoría de las mencionadas aquí
harishr
6

Tengo una pequeña solución que usa plantillas T4 ( ver fuente ).

Pasas de cualquier CLR POCO:

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

A una interfaz de TypeScript:

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

Puede utilizar el proyecto de código abierto NSwag : En la GUI, puede seleccionar la clase .NET de una DLL .NET existente y generar la interfaz TypeScript para ella.

El proyecto también proporciona herramientas de línea de comandos y soporte para plantillas T4, así como generación de código de cliente para controladores Web API ...

Rico Suter
fuente
Tenga en cuenta que NSwag generará archivos TS solo a partir de archivos binarios (también necesita todas las dependencias de sus binarios) o su archivo JSON, que Swagger produce a partir de los archivos binarios.
Alex Klaus
3

Si necesita crear un archivo separado para cada clase / interfaz de TypeScript generada (es decir, en una forma de "clase única por archivo"), puede probar TypeGen . Es una herramienta que puede usar desde la Consola del Administrador de paquetes para generar archivos TypeScript basados ​​en sus clases / enumeraciones de C #. Actualmente admite:

  • exportar enumeraciones; exportar POCO como clases o interfaces TS
  • herencia
  • tipos genéricos
  • tipos de colección / colección anidada

además de algunas características adicionales. También es de código abierto (puedes consultarlo en github ).

JB1
fuente
2

¿Qué tal si fuera de la otra manera?

Echa un vistazo a erecruit TypeScript Translator . Viene con soporte C # listo para usar, pero en realidad está basado en plantillas (usa Nunjucks para renderizar), lo que significa que puede generar cualquier otra cosa: VB.NET, F #, C ++, XML, SQL, lo que sea que pueda codificar con una plantilla.

Funciona como un programa de consola .NET, programa NodeJS (para aquellos que no están en Windows) o como una extensión de Visual Studio , completa con la funcionalidad de generar al guardar. E incluye compatibilidad con MSBuild, solo para hacer feliz a su servidor de compilación. :-)

Fyodor Soikin
fuente
Hice +1 en esto porque realmente es la mejor solución, ya que generar al guardar es la característica principal.
John Zabroski
2

También puede usar esto: https://github.com/pankleks/TypeScriptBuilder

Esta pequeña biblioteca genera una definición de tipo TypeScript basada en tipos de C #. Úselo directamente en su proyecto backend C # para generar código para su proyecto frontend TypeScript. También puede escribir una pequeña aplicación de consola para generar código mediante herramientas de compilación previa.

¡Funciona en el marco de trabajo Full & NET Core!

Instalar por nuget: Install-Package TypeScriptBuilder

Funciones compatibles

  • Resolución de dependencia de tipos
  • Genéricos
  • Tipo de herencia
  • Espacios de nombres (módulos)
  • Enumeraciones
  • Tipos que aceptan valores NULL
  • Conversión de diccionario (a objetos indexados de tipo fuerte TS)
  • Conjunto de atributos de control de generación de código
  • any para tipos que no se pueden convertir

Más descripción: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md

pankleks
fuente
Solo lo probé TypeScriptBuildery se ve excelente hasta ahora; ¡Hurra por la compatibilidad con .NET Core!
Tobias J
1

Los chicos echan un vistazo a https://github.com/reinforcement/ Reinforced.Typings . He estado jugando con plantillas typelite y t4 durante los últimos días y terminé con este proyecto. Es súper simple y funciona como un encanto. Simplemente obtenga el paquete, modifique el archivo de configuración (es como 10 segundos) y compile. Todo se hace automáticamente sin problemas. ¡Bendito sea el autor!

Lo malo de las plantillas T4 es que una vez que construye desde VS, los ensamblajes escaneados se bloquean y debe reiniciar VS (¿qué tan tonto es eso?). Hay algunas soluciones en T4 Toolbox + algunas directivas de limpieza de VS, pero ninguna de ellas funcionó para mí.

veb
fuente
1

Me gusta la solución citykid. Lo había extendido un poco. Entonces, la solución también se basa en la técnica de generación de código con plantillas T4.

Puede generar tipos comunes de TypeScript y declaraciones ambientales.

Admite implementaciones de herencia e interfaz.

Admite genéricos, matrices y listas como campos de tipo.

También se traduce a tipos de TypeScript que no se mencionan explícitamente en la configuración (por ejemplo, importamos el tipo A, y en la salida de TS puede encontrar algunos otros tipos: tipos de campos, tipos base e interfaces).

También puede anular el nombre del tipo.

También se admiten enumeraciones.

Ejemplo de uso (puede encontrarlo en el repositorio del proyecto):

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

Y obtendrás una salida

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

Consulte la solución completa aquí: https://bitbucket.org/chandrush/cst4ts

Piedra rodante
fuente
1

También puede utilizar Bridge.net. A partir de la versión 1.7, admite la generación de definiciones de TypeScript para tipos de C #. Ver http://bridge.net/docs/generate-typescript-definitions/

George Birbilis
fuente
El problema principal es que cuando haces una búsqueda en Google, puedes llegar a la pregunta original y nunca notar que existía información útil en una respuesta duplicada / cerrada
George Birbilis
Así que asegúrese de que el canónico tenga la mejor información. No publiques en varias preguntas. Cualquier discusión adicional sobre esto se puede llevar a Meta Stack Overflow
Martijn Pieters
1

También me gustó mucho la respuesta de @ citykid, así que la extendí para hacer un espacio de nombres completo a la vez. Simplemente coloque las clases POCO en el espacio de nombres y reconstruya las plantillas T4. Desearía saber cómo generar archivos separados para cada uno, pero eso no es el fin del mundo.

Debe hacer referencia a los archivos .DLL en la parte superior (donde están las clases que desea) y debe mencionar los espacios de nombres. Todas las líneas para editar están marcadas con ACME. Gran saludo a @citykid, ¡lo agradezco!

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

Mi solución fue escribir una pequeña utilidad de codegen que simplemente toma un ensamblaje de proyecto (y ensamblajes de referencia) y comienza a escanear tipos que están involucrados en la interacción entre mecanografiado y c #. Esta utilidad genera tanto javascript como d.ts ... La herramienta se llama en el evento posterior a la compilación ... ¡funciona como un encanto!

Paul0515
fuente
No sé por qué elegí generar js y d.ts ... en este momento simplemente genera .ts ... sencillo y muy factible.
Paul0515
1
Hola Paul ... ¿podrías compartir el código util con nosotros? Estoy buscando simplemente crear un archivo ts (en una ubicación de carpeta determinada) y no un archivo d.ts como dijiste. ¿Es eso algo que tu util puede hacer?
Krishna Teja Veeramachaneni
0

Si está interesado, puede utilizar TypedRpc . Su propósito no es solo crear las interfaces en TypeScript, sino crear toda la comunicación con el servicio en .Net utilizando el protocolo JsonRpc.

Ejemplo de una clase en el servidor:

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

Uso del código TypeScript generado:

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

Consulte https://github.com/Rodris/TypedRpc para ver otros ejemplos de cómo usarlo.

Rodris
fuente
0

Los generadores de cliente ASP.NET Web API pueden ser más útiles y menos costosos que la cadena de herramientas swagger y otros durante SDLC.

Si bien los programadores generalmente usan WebApiClientGen para generar códigos API de cliente, este proyecto también proporciona POCO2TS.exe, un programa de línea de comandos que genera interfaces TypsScript a partir de clases POCO. Puede usar Poco2ts.exe o el componente poco2ts para integrar la generación de código con su canal de compilación.

ZZZ
fuente
0

Si desea convertirlo a través de node.js, puede usar este paquete (csharp-to-typescript). https://www.npmjs.com/package/csharp-to-typescript

código fuente: 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
fuente
0

Si está utilizando VSCode, podría echar un vistazo a esa extensión C # a TypeScript

Convierta la clase C # a la interfaz TypeScript

Vea cómo puedo convertirlo de código C # con solo un clic. Por supuesto, no todas las clases se convertirán como la fábrica, pero es lo suficientemente bueno para el lado del cliente. Solo necesitamos la forma de los datos. A veces, si necesito usar el patrón de visitante, lo decoraré con los métodos adicionales que necesite. Solo tomó como 5 segundos hacerlo. Comparando con el minuto anterior, podría decir que es una gran victoria.

Ver más detalles en la publicación de mi blog

trungk18
fuente