javascript - Kuinka käyttää olemassa olevia C#-luokan määritelmiä uudelleen TypeScript-projekteissa


Aion juuri alkaa käyttää TypeScript-kieltä HTML-asiakasprojektissani, joka kuuluu MVC-projektiin, jossa on jo olemassa entity framework -domain-malli. Haluan, että kaksi projektiani (asiakaspuoli ja palvelinpuoli) ovat täysin erillään, koska kaksi tiimiä työskentelee tämän parissa... JSONia ja RESTia käytetään kohteiden viestintään edestakaisin.

Tietenkin minun domain-objektieni asiakaspuolella pitäisi vastata palvelinpuolen objekteja. Aiemmin olen yleensä tehnyt tämän manuaalisesti. Onko olemassa tapa käyttää uudelleen C#-luokkien määritelmiäni (erityisesti POJO-luokkia domain-mallissani) luodakseni vastaavat luokat TypeScriptissä?

Vastaus #1

Tällä hetkellä ei ole mitään, mikä muuntaisi C#:n TypeScriptiksi. Jos sinulla on paljon POCO-luokkia tai uskot, että ne saattavat muuttua usein, voisit luoda muuntimen - jotain yksinkertaista suunnilleen seuraavanlaista...

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

Kahden

export class MyPoco {
    public Name: string;
}

Codeplexilla on myös keskustelua C#:stä automaattisen generoinnin osalta.

Vain pitääksemme asiat ajan tasalla, TypeLite voi luoda TypeScript-rajapintoja C#:sta:

http://type.litesolutions.net/

Vastaus #2

Web Essentialsin avulla voidaan kääntää C#-tiedostot TypeScript .d.ts -tiedostoiksi tallennettaessa. Sitten voit viitata määritelmiin .ts-tiedostoistasi.

Vastaus #3

Jos käytät vscodea, voit käyttää minun laajennustani csharp2ts, joka tekee juuri sen.

Sinun tarvitsee vain valita liitetty C#-koodi ja suorittaa Convert C# to TypeScript -komento komentopalettista

Muunnosesimerkki:

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

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

että

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

    /**Person name */
    Name : string;
}

Vastaus #4

Sekä yllä olevat TypeLite että T4TS näyttivät hyviltä, valitsin vain yhden, TypeLiten, haaroin sen saadakseni tukea

  • Arvotyypit,
  • Nullablet
  • camelCasing (TypeScriptin päädokumentti käyttää kamelia, ja tämä sopii myös hyvin yhteen C#:n kanssa)
  • julkiset kentät (rakastavat puhtaita ja luettavia POCOja, tekee myös C#-kääntäjän työn helpoksi)
  • poista moduulien luonti käytöstä

Sitten tarvitsin C# rajapintoja ja ajattelin, että on aika tehdä oma juttu ja kirjoitin yksinkertaisen T4-skriptin, joka tekee juuri sen, mitä tarvitsen. Se sisältää myös Enumit. Repoa ei tarvita, vain alle 100 riviä T4:ää.

Käyttö
Ei kirjastoa, ei NuGetia, vain tämä yksinkertainen T4-tiedosto - käytä Visual Studiossa "add item" ja valitse mikä tahansa T4-malli. Liitä sitten tämä tiedostoon. Muokkaa jokainen rivi, jossa on "ACME". Lisää jokaista C#-luokkaa varten rivi

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

Järjestyksellä on merkitystä, mitä tahansa tunnettua tyyppiä käytetään seuraavissa rajapinnoissa. Jos käytät vain rajapintoja, tiedostopääte voi olla .d.ts, mutta enum-arvojen kohdalla tarvitset .ts -tiedoston, koska muuttuja luodaan.

Muokkaus
Muokkaa skriptiä.

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

Seuraava taso skriptissä tulee olemaan luoda palvelurajapinta MVC JsonController -luokasta.

Vastaus #5

Kokeile Reinforced.Typings-kehystä. Näyttää siltä, että se ratkaisee ongelmasi.

  1. Asenna se NuGetista
  2. Siirry POCOosi ja lisää [TsInterface]-attribuutti sen yläpuolelle

    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. Rakenna projektisi uudelleen
  4. Etsi luotu TypeScript-koodi %Your_Project_Directory%/Scripts/project.ts-tiedostosta ja lisää se projektiin manuaalisesti

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
    
  5. Tee sama kaikille POCOillesi ja viittaa project.ts tiedostoon muussa TypeScript-koodissasi.

Katso lisää tietoja dokumentaation wikistä

Vastaus #6

Tässä on lähestymistapani sen ratkaisemiseksi. Määrittele C#-luokkasi attribuutilla, ja .d.ts-tiedostot luodaan (käyttäen T4-muunnoksia). Nugetissa on paketti ja lähdekoodi on saatavilla GitHubissa. Työskentelen edelleen projektin parissa, mutta tuki on varsin laaja.

Vastaus #7

Jos käytät Visual Studiota, lisää Typewriter-laajennus.

Visual Studio -galleria

Verkkosivusto/Dokumentaatio

Päivitys

Kun Web Essentials on asennettuna VS 2015:ssä, voit napsauttaa luokkataulua hiiren oikealla, sitten > Web Essentials > Luo Typescript Intellisense -tiedosto kontekstivalikosta.

Vastaus #8

Olen luonut pienen työkalun, joka voi luoda TypeScript-rajapintoja C#-luokista. Se on saatavilla NuGet-pakettina. Yksityiskohtainen dokumentaatio löytyy projektin verkkosivuilta.

Aiheeseen liittyviä artikkeleita