javascript - Mevcut C# sınıf tanımlarını TypeScript projelerinde nasıl yeniden kullanabilirim


Ben sadece, zaten bir varlık çerçevesi (entity framework) alan modeline sahip bir MVC projesine ait HTML istemci projemde TypeScript kullanmaya başlamak üzereyim. İki projemin (istemci tarafı ve sunucu tarafı) tamamen ayrı olmasını istiyorum çünkü iki farklı ekip üzerinde çalışacak... Nesneleri karşılıklı iletmek için JSON ve REST kullanılıyor.

Tabii ki, istemci tarafındaki domain nesnelerimin sunucu tarafındaki nesnelerle eşleşmesi gerekir. Geçmişte, bunu genellikle manuel olarak yapardım. C# sınıf tanımlarımı (özellikle domain modelimdeki POJO sınıflarının) TypeScript'te karşılık gelen sınıfları oluşturmak için yeniden kullanmanın bir yolu var mı?

Cevap #1

Şu anda C#'ı TypeScript'e eşleyecek herhangi bir şey yok. Eğer çok sayıda POCO'nuz varsa veya sık sık değişebileceklerini düşünüyorsanız, basit bir şekilde bunu yapan bir dönüştürücü oluşturabilirsiniz...

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

Kime

export class MyPoco {
    public Name: string;
}

C#'tan otomatik oluşturma hakkında Codeplex'te de bir tartışma var.

Durumu güncel tutmak için, TypeLite C#'tan TypeScript arayüzleri oluşturabilir:

http://type.litesolutions.net/

Cevap #2

Web Essentials, C# dosyalarını kaydetme sırasında TypeScript .d.ts dosyalarına derlemeyi sağlar. Daha sonra bu tanımları .ts dosyalarınızdan referans alabilirsiniz.

Cevap #3

Eğer vscode kullanıyorsanız, tam olarak bunu yapan csharp2ts adlı uzantımı kullanabilirsiniz.

Yapıştırılan C# kodunu seçip komut paletinden C#'ı TypeScript'e Dönüştür komutunu çalıştırmanız yeterlidir

Bir dönüşüm örneği:

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

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

için

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

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

Cevap #4

Yukarıdaki TypeLite ve T4TS ikisi de iyi görünüyordu, sadece birini seçtim, TypeLite, destek almak için çatalladım

  • DeğerTürleri,
  • Nullable'lar
  • camelCasing (TypeScript kök dokümanı camelları kullanıyor ve bu C# ile de çok güzel uyum sağlıyor)
  • genel alanlar (temiz ve okunabilir POCO’ları sever, ayrıca C# Derleyicisi için de kolaylaştırır)
  • modül üretimini devre dışı bırak

Sonra C# arayüzlerine ihtiyacım vardı ve kendi şeyimi yapma zamanı geldiğini düşündüm ve sadece ihtiyacım olanı yapan basit bir T4 betiği yazdım. Ayrıca Enumları da içeriyor. Repo gerekmez, sadece 100 satırın altında T4.

Kullanım
Kütüphane yok, NuGet yok, sadece bu basit T4 dosyası - Visual Studio'da "öğe ekle"yi kullanın ve herhangi bir T4 şablonu seçin. Sonra bunu dosyaya yapıştırın. İçinde "ACME" geçen her satırı uyarlayın. Her C# sınıfı için bir satır ekleyin

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

Sıra önemlidir, bilinen herhangi bir tür aşağıdaki arayüzlerde kullanılacaktır. Sadece arayüzleri kullanırsanız, dosya uzantısı .d.ts olabilir, enumlar için bir değişken örneğe alındığından .ts dosyasına ihtiyacınız vardır.

Özelleştirme
Betiği hackle.

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

Betiğin bir sonraki seviyesi, MVC JsonController sınıfından servis arayüzünü oluşturmaktır.

Cevap #5

Reinforced.Typings çerçevesini deneyin. Görünüşe göre bu sorununuzu çözüyor.

  1. Bunu NuGet'ten yükleyin
  2. POCO'nuza gidin ve üstüne [TsInterface] özniteliğini ekleyin

    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. Projenizi yeniden oluşturun
  4. Oluşturulan TypeScript kodunu %Your_Project_Directory%/Scripts/project.ts dosyasında bulun ve projeye manuel olarak ekleyin

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
    
  5. Tüm POCO'larınız için aynısını yapın ve diğer TypeScript kodunuzda project.ts'i referans gösterin.

Daha fazla detayı belgelendirme wikinde görebilirsin

Cevap #6

İşte bunu çözme konusundaki yaklaşımım. C# sınıflarınızı bir öznitelik ile tanımlayın ve .d.ts dosyaları oluşturulacaktır (T4 dönüşümleri kullanılarak). Nuget üzerinde bir paket var ve kaynak kodu Github'da mevcut. Hâlâ proje üzerinde çalışıyorum, ama destek oldukça kapsamlı.

Cevap #7

Eğer Visual Studio kullanıyorsanız, Typewriter eklentisini ekleyin.

Visual Studio Galerisi

Web Sitesi/Dökümantasyon

Güncelleme

VS 2015'te Web Essentials yüklü olduğunda, sınıf dosyasına sağ tıklayabilir, ardından> Web Essentials > Bağlam menüsünden Typescript Intellisense Dosyası Oluştur seçeneğini seçebilirsiniz.

Cevap #8

C# sınıflarından TypeScript arayüzleri oluşturabilen küçük bir yardımcı program oluşturdum. NuGet paketi olarak mevcuttur. Ayrıntılı belgeler proje web sayfasında bulunabilir.

İlgili makaleler