วิธีการนำนิยามคลาส C # ที่มีอยู่กลับมาใช้ใหม่ในโปรเจ็กต์ TypeScript


122

ฉันจะเริ่มใช้TypeScriptในโปรเจ็กต์ไคลเอนต์ HTML ของฉันซึ่งเป็นของโครงการ MVC ที่มีโมเดลโดเมนเฟรมเวิร์กเอนทิตีอยู่แล้ว ฉันต้องการให้สองโปรเจ็กต์ของฉัน (ฝั่งไคลเอ็นต์และฝั่งเซิร์ฟเวอร์) แยกออกจากกันโดยสิ้นเชิงเนื่องจากทั้งสองทีมจะทำงานกับสิ่งนี้ ... JSON และ REST ใช้เพื่อสื่อสารอ็อบเจกต์ไปมา

แน่นอนว่าdomainวัตถุของฉันในฝั่งไคลเอ็นต์ควรตรงกับวัตถุทางฝั่งเซิร์ฟเวอร์ ที่ผ่านมาฉันทำสิ่งนี้ด้วยตนเองเป็นปกติ มีวิธีการนำนิยามคลาส C # ของฉันกลับมาใช้ซ้ำ (โดยเฉพาะของPOJOคลาสในโมเดลโดเมนของฉัน) เพื่อสร้างคลาสที่สอดคล้องกันใน TypeScript "หรือไม่


ไม่มันเป็นภาษาที่แตกต่างกันโดยสิ้นเชิง
Hans Passant

6
ใช่ฉันหมายถึง POCO ฉันรู้ว่ามันเป็นภาษาที่แตกต่างกันโดยสิ้นเชิง ... แต่มันจะเป็นการดีที่จะหลีกเลี่ยงการพิมพ์ทั้งหมดนี้อีกครั้ง ... ฉันคิดว่าสักครู่ฉันจะคัดลอกและวางรหัส c # ของฉัน .. และจะ refactor อะไรก็ได้จนกว่ามันจะรวมเข้าด้วยกัน TypeScript ... ด้วยวิธีนี้ฉันแน่ใจว่าฉันไม่ได้พิมพ์แอตทริบิวต์ผิด .. และช่วยให้ฉันไม่ต้องจำ แต่สิ่งนี้ฟังดูไม่ดีเลย
pabloelustondo

1
ฉันสนใจในทิศทางอื่นด้วยเช่นจากคลาสโมเดล typescript ไปจนถึงคลาสโมเดล C # ซึ่งสามารถอธิบายได้แบบสองทิศทางผ่าน JSON
Jason Kleban

ฉันมองหาสิ่งเดียวกันและฉันเพิ่งสะดุดกับปลั๊กอิน Gulp นี้แม้ว่าฉันจะไม่รู้ว่ามีการสนับสนุนมากแค่ไหนก็ตาม
Luke T O'Brien

คำตอบ:


54

ขณะนี้ยังไม่มีสิ่งใดที่จะแมป C # กับ TypeScript หากคุณมี POCO จำนวนมากหรือคุณคิดว่าอาจมีการเปลี่ยนแปลงบ่อยๆคุณสามารถสร้างตัวแปลง - สิ่งง่ายๆตามแนว ...

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

ถึง

export class MyPoco {
    public Name: string;
}

นอกจากนี้ยังมีการอภิปรายเกี่ยวกับ Codeplex เกี่ยวกับรถยนต์ที่ก่อจาก C #

เพื่อให้สิ่งต่าง ๆ อัปเดต TypeLite สามารถสร้างอินเตอร์เฟส TypeScript จาก C #:

http://type.litesolutions.net/


เย้ปอะไรทำนองนี้คิดจะทำด้วยตนเอง.. ดีจัง. ไม่แน่ใจว่าตอนนี้ฉันมีเวลาเขียนตัวแปลง .. แต่ใช่ว่าจะเป็นความคิด ขอบคุณ อาจเป็นเพราะ Microsoft กำลังทำอยู่ตอนนี้ในขณะที่เราพูด
pabloelustondo

คุณสามารถทำได้สองวิธี ... สร้างไฟล์. ts ด้วยความช่วยเหลือของ EnvDTE Automation หรือโดยใช้ Mef Directory Catalog และ Reflection / Introspection ฉันชอบอันที่สองมากกว่าเพราะมันไม่ได้ขึ้นอยู่กับภาพ
Arek Bal

1
ฉันเริ่มใช้โผและพบปัญหาเดียวกันกับวิธีการแชร์ประเภทระหว่าง c # และ javascript ฉันหวังว่าปัญหานี้จะแก้ไขได้โดย typescript แต่ดูเหมือนว่ายังไม่มีคำตอบ ฉันรู้สึกเสียไปเล็กน้อยโดยคอมไพเลอร์ saltarelle ซึ่งรวบรวม c # เป็น javascript ค่อนข้างดีsaltarelle-compiler.com
BraveNewMath

2
@DanielHarvey Enums ถูกสร้างขึ้นอย่างถูกต้องโดย TypeLite อาจได้รับการแก้ไขตั้งแต่ความคิดเห็นของคุณ
pauloya

1
TypeScriptPaste จะแปลง C # เป็นTypescript คุณต้องมีคอมไพเลอร์ Roslyn ซึ่งหมายถึง Visual Studio 2015 แต่มันใช้งานได้ดีจริงๆ มันไม่ใช่ 100% และฉันต้องโง่ลงโค้ด - การแปลง foreach เป็นลูปเช่นการเขียนคลาส List <T> ของฉันเองและคุณต้องจัดโครงสร้างโค้ดของคุณเพื่อให้ 'บริสุทธิ์' โดยมีโครงสร้างข้อมูลภายในเท่านั้น ใช้งานได้ แต่มันเป็นราคาเล็กน้อยที่ต้องจ่ายสำหรับความสามารถในการแก้ไขและทดสอบโค้ดใน VS จากนั้น 'คอมไพล์' เป็น Javascript ผ่าน TypeScript
John Mott

37

Web Essentialsอนุญาตให้คอมไพล์ไฟล์ C # เป็นไฟล์ TypeScript .d.tsเมื่อบันทึก จากนั้นคุณสามารถอ้างอิงคำจำกัดความจาก.tsไฟล์ของคุณ

ใส่คำอธิบายภาพที่นี่


คุณอาจสับสนไฟล์. ts และ c # หรือไม่? ไม่พบตัวเลือกนี้
Flores

1
แต่หลังจากการค้นหาบางครั้งฉันพบว่าคุณหมายถึงอะไร .. มีประโยชน์มากจริงๆ
Flores

2
จริงๆแล้วไม่ใช่ฉันค่อนข้างแน่ใจว่าการดำเนินการ 'รวบรวม' ไม่มีอะไรมันสร้างรหัส นั่นทำให้ฉันสับสน แต่ไม่ถูกต้อง 99% ฉันจะให้สิ่งนั้น
Flores

1
จะมีประโยชน์มากขึ้นหากเราสามารถกำหนดค่าเนมสเปซและการปรับแต่งบางอย่างได้
Farshid Saberi

5
ตอนนี้คุณต้องติดตั้ง 'TypeScript Definition Generator' ด้วยตนเองเพื่อใช้สิ่งนี้ - ดูที่นี่
Taran

25

TypeLite และ T4TS ด้านบนทั้งคู่ดูดีเพียงแค่เลือกหนึ่ง TypeLite แยกมันเพื่อรับการสนับสนุนสำหรับ

  • ValueTypes ,
  • Nullables
  • camelCasing (TypeScript root doc ใช้อูฐและสิ่งนี้ก็เข้ากันได้ดีกับ C #)
  • ฟิลด์สาธารณะ (รัก POCO ที่สะอาดและอ่านง่ายยังช่วยให้คอมไพเลอร์ C # เป็นเรื่องง่าย)
  • ปิดใช้งานการสร้างโมดูล

จากนั้นฉันก็ต้องการอินเทอร์เฟซ C # และคิดว่าถึงเวลาที่ต้องอบสิ่งของของตัวเองและเขียนสคริปต์ T4 ง่ายๆที่ทำในสิ่งที่ฉันต้องการ นอกจากนี้ยังรวมถึงEnums Enumsไม่จำเป็นต้องใช้ repo เพียง <100 บรรทัดของ T4

การใช้งาน
ไม่มีไลบรารีไม่มี NuGet เพียงแค่ไฟล์ T4 ธรรมดา ๆ นี้ - ใช้ "เพิ่มรายการ" ใน Visual Studio และเลือกเทมเพลต T4 ใดก็ได้ จากนั้นวางลงในไฟล์ ปรับทุกบรรทัดด้วย "ACME" ในนั้น สำหรับทุกชั้น C # เพิ่มบรรทัด

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

การสั่งซื้อมีความสำคัญประเภทใด ๆ ที่รู้จักจะถูกใช้ในอินเตอร์เฟส follwing หากคุณใช้เฉพาะอินเทอร์เฟซนามสกุลไฟล์อาจเป็น. d.tsสำหรับ enums คุณต้องมีไฟล์. tsเนื่องจากตัวแปรถูกสร้างอินสแตนซ์

การปรับแต่ง
แฮ็คสคริปต์

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

ระดับถัดไปของสคริปต์คือการสร้างส่วนต่อประสานบริการจากคลาส MVC JsonController


1
น่ารักมาก แต่คุณมีtypeof(ParticleKind)อยู่ในEnum<T>ตัว แน่นอนว่าควรจะเป็นtypeof(T)อย่างไร? นอกจากนี้คุณต้องอัปเดตboolให้เป็นbooleanTypeScript รุ่นที่ใหม่กว่า
Gone Coding

โปรดทราบ: สิ่งนี้แตกด้วยคุณสมบัติ enum ในคลาส
Gone Coding

1. เอ่อใช่ขอบคุณสำหรับหัวขึ้นจะแก้ไขได้ 2. typescript มีวิวัฒนาการมาตั้งแต่เขียนสคริปต์นี้ ดูเหมือนว่าอาจต้องมีการอัปเดตคุณพูดถูกอย่างแน่นอน ฉันหวังว่ามันจะเป็นจุดเริ่มต้นสำหรับจุดประสงค์ของคุณเอง
citykid

1
ผมแปลงมันเข้าไปในห้องสมุดผู้ช่วย, แก้ไขข้อบกพร่องและตอนนี้ใช้เพียงแค่รายการเดียวบรรทัดในไฟล์ TT ของฉันเพื่อสร้างอินเตอร์เฟซใหม่และconst enums ขอบคุณสำหรับตัวอย่าง มันใกล้มากและทำให้ฉันทำงานได้เร็วขึ้นมาก
Gone Coding

25

หากคุณใช้ vscode คุณสามารถใช้csharp2tsส่วนขยายของฉันซึ่งทำอย่างนั้นได้

คุณเพียงแค่เลือกรหัส C # ที่วางและเรียกใช้Convert C# to TypeScriptคำสั่งจากชุดคำสั่ง ใส่คำอธิบายภาพที่นี่ ตัวอย่างการแปลง:

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
ฉันคิดว่าผู้คนสนใจโซลูชันอัตโนมัติเต็มรูปแบบซึ่งการรวบรวมด้วย msbuild จะแสดงคำจำกัดความ typescript ก่อนที่จะบริโภค มีวิธีรับสิ่งนั้นโดยใช้ปลั๊กอินหรือไม่?
binki

ไม่นี่เป็นปลั๊กอิน VSCode ใช้งานได้เฉพาะในโปรแกรมแก้ไขและไม่ใช่โปรแกรมแบบสแตนด์อะโลน
Rafael

ฉันได้แปลงส่วนขยาย vscode นี้เป็นโมดูลโหนดหากคุณต้องการผ่าน node.js หรือ tytpescript คุณสามารถตรวจสอบ repo ได้ที่นี่: github.com/YuvrajSagarRana/csharp-to-typescript
Sagar Rana Magar

มีวิธีเชื่อมโยงคำสั่งกับแป้นพิมพ์ลัดหรือไม่? ตัวอย่างเช่นจะเลือก. net code และมี setup shift + ctrl + c เพื่อเรียกใช้งาน "Convert C # to TypeScript" ซึ่งจะทำให้กระบวนการเร็วขึ้น ฉันชอบปลั๊กอิน!
Pawel Cioch

19

หากคุณใช้ Visual Studio ให้เพิ่มส่วนขยายเครื่องพิมพ์ดีด

แกลเลอรี Visual Studio

เว็บไซต์ / เอกสาร

ปรับปรุง

เมื่อติดตั้งWeb Essentialsใน VS 2015 คุณสามารถคลิกขวาที่ไฟล์คลาสจากนั้น> Web Essentials> สร้างไฟล์ typescript Intellisense จากเมนูบริบท


มันสร้างไฟล์ d.ts สำหรับการสนับสนุน intellisense แต่จะใช้อย่างไรแล้วความสัมพันธ์กับการสร้างคลาสพื้นฐานคืออะไร? ฉันไปที่ WebEssentails แล้วไม่พบเอกสารใด ๆ ข้อเสนอแนะใด ๆ ? TIA
howardlo

@hio เพียงอ้างอิงไฟล์ d.ts ในไฟล์. ts ที่คุณต้องใช้ในเมื่อ / ถ้าคุณเปลี่ยนคลาสไฟล์ d.ts ที่อยู่ภายใต้จะอัปเดต (เคล็ดลับ: คุณสามารถลากและวาง d.ts ไว้ที่ด้านบนสุดของไฟล์. ts และจะสร้างข้อมูลอ้างอิงให้คุณ)
Michael Tranchida

ขอบคุณ Michael! ฉันลองวางไฟล์ d.ts ลงในไฟล์. ts ที่ว่างเปล่าและเพิ่มไฟล์ลงในโฟลเดอร์ที่มีเท่านั้น ทดลองใช้ใน VS2015 และ VS2017 ใหม่ผลลัพธ์เดียวกัน คุณรู้จักวิดีโอหรือบทช่วยสอนที่แสดงให้เห็นว่าทั้งหมดนี้ทำงานอย่างไร TIA
howardlo

ฉันลองวางไฟล์ d.ts ลงในไฟล์. ts ในโปรแกรมแก้ไขและสร้างข้อมูลอ้างอิง ฉันคิดว่าฉันเข้าใจแล้ว การสอนยังคงยินดี ขอบคุณ!
howardlo

@hlo ขออภัยไม่ทราบบทเรียนใด ๆ เมื่อฉันไปเส้นทาง Web Essentials ฉันได้สร้างไฟล์. ts ที่ซ้ำกันเนื่องจากโมเดลโดเมนของฉันอยู่ในไลบรารีคลาสและในโค้ดฝั่งไคลเอ็นต์ของฉันฉันมักจะต้องสร้างโมเดล dto หรือ vm ซึ่งเบราว์เซอร์ต้องใช้ รู้เกี่ยวกับ. หากคุณประสบปัญหาทั้งหมดนี้ให้ลองใช้ส่วนขยายเครื่องพิมพ์ดีด ช่วยให้คุณมีไฟล์โมเดล. ts ได้ทุกที่ที่คุณต้องการ
Michael Tranchida

18

นี่คือแนวทางของฉันในการแก้ปัญหา ประกาศคลาส C # ของคุณด้วยแอตทริบิวต์และไฟล์. d.ts จะถูกสร้างขึ้น (โดยใช้การแปลง T4) มีเป็นแพคเกจใน nugetและแหล่งที่มาที่มีอยู่บน GitHub ฉันยังคงทำงานในโครงการนี้ แต่การสนับสนุนค่อนข้างครอบคลุม


ฉันแยกโครงการนี้และเพิ่มการสนับสนุนที่น่าพิศวงที่นี่: github.com/omniscient/t4ts
Frank.Germain

15

ลอง Reinforced.Typings framework ดูเหมือนว่าจะช่วยแก้ปัญหาของคุณได้

  1. ติดตั้งจากNuGet
  2. ไปที่ POCO ของคุณและเพิ่ม[TsInterface]แอตทริบิวต์ด้านบน

    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. สร้างโครงการของคุณใหม่
  4. ค้นหารหัส TypeScript ที่สร้างขึ้นใน%Your_Project_Directory%/Scripts/project.tsไฟล์และเพิ่มลงในโครงการด้วยตนเอง

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
  5. ทำเช่นเดียวกันสำหรับproject.tsPOCO ทั้งหมดของคุณและการอ้างอิงในรหัส TypeScript อื่น ๆ ของคุณ

ดูรายละเอียดเพิ่มเติมในเอกสารวิกิ


เพิ่มข้อมูลโค้ดตัวอย่างแทนที่จะแชร์เฉพาะ URL
Venkat.R

มีวิกิพร้อม Quickstart O_o github.com/reinforced/Reinforced.Typings/wiki
Pavel B.Novikov

แต่โอเคฉันจะยกตัวอย่างที่นี่ :)
Pavel B. Novikov

ไม่รองรับ. NET Core ( ลิงค์ ) :(
Alex Klaus

6
@AlexKlaus .NET การสนับสนุนหลักมาแล้ว
Pavel B. Novikov

10

ฉันได้สร้างยูทิลิตี้ขนาดเล็กที่สามารถสร้างอินเตอร์เฟส TypeScript จากคลาส C # สามารถใช้ได้เป็นแพคเกจ NuGet เอกสารรายละเอียดที่สามารถพบได้บนหน้าเว็บโครงการ


ห้องสมุดสวยความพยายามดี น่าเสียดายที่ไม่พบวิธีปรับแต่งประเภทค่าที่สร้างขึ้นเช่นแปลง "string" เป็น "KnockoutObservableString" หรือ "string []" เป็น "KnockoutObservableArray" เป็นสิ่งที่อยู่ในแผนอนาคตอันใกล้นี้หรือไม่?
รูท

2
ฉันใช้ T4TS แทนใช้เวลา 14 นาที
รูท

@root คุณไม่ควรแปลงสตริง [] เป็น KnockoutObservableArray ในคำจำกัดความที่สร้างขึ้น คุณใช้คำจำกัดความที่สร้างขึ้นเหล่านี้เมื่อคุณใช้งานอินสแตนซ์แบบอนุกรมของคลาสเหล่านี้จริงๆ ตอนนี้คุณอาจเรียกใช้วัตถุทั้งหมดผ่านปลั๊กอินการแมป แต่ฉันจะระวังสิ่งนี้ นั่นอาจมากเกินไปและฉันมักจะชอบแปลงด้วยตนเอง (อาจผ่าน ctor) จากอินสแตนซ์คลาส C # ที่ต่อเนื่องเป็นคลาส TypeScript ที่เต็มไปด้วยสิ่งที่สังเกตได้ ครั้งเดียวที่ฉันแนะนำให้เรียกใช้อย่างไม่ชัดเจนผ่านปลั๊กอินการทำแผนที่สำหรับแอปสไตล์ CRUD
Allen Rice

@Lukas Kabrt ขอบคุณมากสำหรับ TypeLite มันมีประโยชน์อย่างมากในโครงการของเราและฉันเขียนเกี่ยวกับเรื่องนี้สั้น ๆ ที่นี่: ใต้น้ำ
Allen Rice

@AllenRice ฉันพบว่ามีประโยชน์ที่จะมีคลาสโมเดลมุมมอง Knockout ที่สร้างขึ้นด้วยมือซึ่งเป็นที่ที่ฉันใช้วิธีการที่คุณระบุและรวมเข้ากับคลาสที่สร้างขึ้นโดยอัตโนมัติซึ่งเป็น DTO ที่ส่งผ่านปลั๊กอินการทำแผนที่
รูท

7

โปรดดูที่เครื่องพิมพ์ดีดห้องสมุดนี้

ไม่เพียงแปลงคลาส enums อินเทอร์เฟซและอื่น ๆ แต่ยังรวมถึงตัวควบคุม api ซึ่งยอดเยี่ยมมาก ..

นอกจากนี้ยังทำได้ทันทีที่คุณบันทึกไฟล์. cs ต้นทางดังนั้นฉันจึงไม่ต้องทริกเกอร์เครื่องมือภายนอกใด ๆ บันทึก. cs และคุณจะได้รับการปรับปรุง. ts


สิ่งที่ยอดเยี่ยมคือสร้างไฟล์ TS ในการบันทึกไฟล์ CS ที่เกี่ยวข้อง อย่างไรก็ตามการตั้งค่าโฟลเดอร์ผลลัพธ์ค่อนข้างเปราะบาง (ดูการอภิปรายที่นี่ ) และต้องใช้รหัสที่สร้างขึ้นด้วยมือ
Alex Klaus

ฉันใช้เครื่องมือสร้างเพื่อสิ่งนั้นและเครื่องมือนี้มีประสิทธิภาพมากกว่าที่กล่าวถึงมากที่สุดในที่นี้
harishr

6

ฉันมีวิธีแก้ปัญหาเล็กน้อยที่ใช้เทมเพลต T4 ( ดูแหล่งที่มา )

คุณไปจาก CLR POCO ใด ๆ :

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

ไปยังอินเทอร์เฟซ TypeScript:

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

3

คุณสามารถใช้NSwagโปรเจ็กต์โอเพนซอร์สได้ : ใน GUI คุณสามารถเลือกคลาส. NET จาก. NET DLL ที่มีอยู่และสร้างอินเทอร์เฟซ TypeScript สำหรับมัน

โครงการนี้ยังมีเครื่องมือบรรทัดคำสั่งและการสนับสนุนเทมเพลต T4 ตลอดจนการสร้างรหัสไคลเอนต์สำหรับตัวควบคุม Web API ...


โปรดทราบว่า NSwag จะสร้างไฟล์ TS จากไฟล์ไบนารีเท่านั้น (คุณต้องมีการอ้างอิงทั้งหมดของไบนารีของคุณด้วย) หรือไฟล์ JSON ซึ่ง Swagger สร้างจากไฟล์ไบนารี
Alex Klaus

3

หากคุณจำเป็นต้องสร้างไฟล์ที่แยกต่างหากสำหรับทุกสร้าง typescript ระดับ / อินเตอร์เฟซ (เช่นใน "ชั้นเดียวต่อไฟล์" ลักษณะ) คุณสามารถลองTypeGen เป็นเครื่องมือที่คุณสามารถใช้จาก Package Manager Console เพื่อสร้างไฟล์ TypeScript ตาม C # คลาส / enums ของคุณ ปัจจุบันรองรับ:

  • การส่งออก enums; การส่งออก POCO เป็นคลาส TS หรืออินเทอร์เฟซ
  • มรดก
  • ประเภททั่วไป
  • คอลเลกชัน / ประเภทคอลเลกชันที่ซ้อนกัน

พร้อมคุณสมบัติเพิ่มเติมบางอย่าง นอกจากนี้ยังเป็นโอเพนซอร์ส (คุณสามารถตรวจสอบได้จากgithub )


2

แล้วทางอื่นล่ะ?

ตรวจสอบerecruit TypeScript Translatorนักแปล มันมาพร้อมกับการรองรับ C # ที่พร้อมใช้งาน แต่จริงๆแล้วเป็นแบบเทมเพลต (ใช้ Nunjucks ในการเรนเดอร์) ซึ่งหมายความว่ามันสามารถสร้างสิ่งอื่นได้ - VB.NET, F #, C ++, XML, SQL - อะไรก็ตามที่คุณสามารถเข้ารหัสด้วยเทมเพลต

ทำงานเป็นโปรแกรมคอนโซล. NET, โปรแกรม NodeJS (สำหรับผู้ที่ไม่ได้ใช้ Windows) หรือเป็นส่วนขยาย Visual Studioพร้อมด้วยฟังก์ชันการสร้างเมื่อบันทึก และรวมถึงการสนับสนุน MSBuild เพียงเพื่อให้บิลด์เซิร์ฟเวอร์ของคุณมีความสุข :-)


ฉัน +1 สิ่งนี้เพราะมันเป็นวิธีแก้ปัญหาที่ดีที่สุดการสร้าง - บันทึกเป็นคุณสมบัติของนักฆ่า
John Zabroski

2

คุณยังสามารถใช้สิ่งนี้: https://github.com/pankleks/TypeScriptBuilder

ไลบรารีขนาดเล็กนี้สร้างนิยามประเภท TypeScript ตามประเภท C # ใช้โดยตรงในโปรเจ็กต์ C # แบ็กเอนด์ของคุณเพื่อสร้างโค้ดสำหรับโปรเจ็กต์ TypeScript ส่วนหน้าของคุณ คุณยังสามารถเขียนแอปคอนโซลขนาดเล็กเพื่อสร้างโค้ดโดยใช้เครื่องมือสร้างล่วงหน้า

ทำงานบนเฟรมเวิร์ก Full & NET Core!

ติดตั้งโดย nuget: Install-Package TypeScriptBuilder

คุณสมบัติที่รองรับ

  • การแก้ไขการพึ่งพาประเภท
  • generics
  • ประเภทการสืบทอด
  • เนมสเปซ (โมดูล)
  • enums
  • ประเภท Nullable
  • การแปลงพจนานุกรม (ไปยังวัตถุที่จัดทำดัชนี TS ประเภทที่แข็งแกร่ง)
  • ชุดแอตทริบิวต์การควบคุมการสร้างรหัส
  • any สำหรับประเภทที่ไม่สามารถแปลงได้

คำอธิบายเพิ่มเติม: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md


เพิ่งลองใช้TypeScriptBuilderและดูดีมาก ไชโยสำหรับการสนับสนุน. NET Core!
Tobias J

1

Guys ดูได้ที่https://github.com/reinforced/ Reinforced.Typings Reinforced.Typingsฉันเล่นกับเทมเพลต typelite และ t4 ในช่วงสองสามวันที่ผ่านมาและจบลงด้วยโครงการนี้ มันง่ายมากและใช้งานได้เหมือนมีเสน่ห์ เพียงแค่รับแพ็คเกจแก้ไขไฟล์คอนฟิกูเรชัน (ใช้เวลา 10 วินาที) และสร้าง ทั้งหมดทำได้โดยอัตโนมัติโดยไม่มีปัญหาใด ๆ อวยพรผู้เขียน!

สิ่งที่แย่เกี่ยวกับเทมเพลต T4 คือเมื่อคุณสร้างจาก VS แล้วแอสเซมบลีที่สแกนจะถูกล็อคและคุณต้องรีสตาร์ท VS (โง่แค่ไหน?) มีวิธีแก้ปัญหาบางอย่างใน T4 Toolbox + คำสั่งการทำความสะอาด VS แต่ไม่มีวิธีใดที่ใช้ได้ผลสำหรับฉัน


1

ฉันชอบการแก้ปัญหาของ citykid ฉันได้ขยายมันออกไปเล็กน้อย ดังนั้นการแก้ปัญหายังขึ้นอยู่กับเทคนิคการสร้างโค้ดด้วยเทมเพลต T4

สามารถสร้างประเภท TypeScript ทั่วไปและการประกาศโดยรอบ

สนับสนุนการสืบทอดและการใช้งานอินเทอร์เฟซ

รองรับ generics อาร์เรย์และรายการเป็นฟิลด์ประเภท

นอกจากนี้ยังแปลเป็นประเภท TypeScript ที่ไม่ได้กล่าวถึงอย่างชัดเจนในการกำหนดค่า (ตัวอย่างเช่นเรานำเข้าประเภท A และในเอาต์พุต TS คุณจะพบประเภทอื่น ๆ ได้แก่ ประเภทของฟิลด์ประเภทฐานและอินเทอร์เฟซ)

คุณยังสามารถแทนที่ชื่อประเภทได้

นอกจากนี้ยังรองรับ Enums

ตัวอย่างการใช้งาน (คุณสามารถค้นหาได้ในที่เก็บโครงการ):

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

และคุณจะได้ผลลัพธ์

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

ตรวจสอบโซลูชันฉบับเต็มได้ที่นี่: https://bitbucket.org/chandrush/cst4ts


1

คุณยังสามารถใช้ Bridge.net จากเวอร์ชัน 1.7 รองรับการสร้างคำจำกัดความ TypeScript สำหรับประเภท C # ดูhttp://bridge.net/docs/generate-typescript-definitions/


ปัญหาหลักคือเมื่อคุณค้นหาโดย Google คุณอาจพบคำถามเดิมและไม่เคยสังเกตว่ามีข้อมูลที่เป็นประโยชน์ในคำตอบที่ซ้ำกัน / ปิด
George Birbilis

ดังนั้นตรวจสอบให้แน่ใจว่า Canonical มีข้อมูลที่ดีที่สุด อย่าโพสต์คำถามหลาย ๆ คำถาม การอภิปรายเพิ่มเติมเกี่ยวกับเรื่องนี้สามารถนำไปที่Meta Stack Overflow
Martijn Pieters

1

ฉันชอบคำตอบของ @ citykid เป็นอย่างมากดังนั้นฉันจึงขยายเพื่อทำเนมสเปซทั้งหมดในแต่ละครั้ง เพียงใส่คลาส POCO ลงในเนมสเปซและสร้างเทมเพลต T4 ขึ้นมาใหม่ ฉันหวังว่าฉันจะรู้วิธีสร้างไฟล์แยกกันสำหรับแต่ละไฟล์ แต่นั่นไม่ใช่จุดจบของโลก

คุณต้องอ้างอิงไฟล์. DLL ในส่วนบนสุด (ซึ่งเป็นคลาสที่คุณต้องการ) และคุณต้องพูดถึงเนมสเปซ ทุกบรรทัดที่จะแก้ไขมีเครื่องหมาย ACME ขอขอบคุณที่ @citykid ขอขอบคุณ!

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

วิธีแก้ปัญหาของฉันคือการเขียนเครื่องมือ codegen ขนาดเล็กที่ใช้เพียงแค่การประกอบโครงการ (และการอ้างอิงแอสเซมบลี) และเริ่มการสแกนประเภทที่เกี่ยวข้องกับการโต้ตอบระหว่าง typescript และ c # โปรแกรมอรรถประโยชน์นี้ส่งออกทั้งจาวาสคริปต์เป็น d.ts ... เครื่องมือนี้เรียกว่าในเหตุการณ์หลังการสร้าง ... ทำงานได้อย่างมีเสน่ห์!


ฉันไม่รู้ว่าทำไมฉันถึงเลือกที่จะส่งออก js และ d.ts ... ตอนนี้มันออกมาเป็น. ts ... ตรงไปตรงมาและทำได้มาก
ปอ 05 15

1
สวัสดีพอล ... คุณจะแชร์รหัสยูทิลกับเราได้ไหม ฉันต้องการสร้างไฟล์ ts (ที่ตำแหน่งโฟลเดอร์บางตำแหน่ง) ไม่ใช่ไฟล์ d.ts อย่างที่คุณพูด สิ่งที่คุณสามารถทำได้หรือไม่?
กฤษณะเตชะวีระมหาเสนี

0

หากสนใจคุณสามารถใช้TypedRpc จุดประสงค์ไม่เพียง แต่สร้างอินเทอร์เฟซใน TypeScript แต่เพื่อสร้างการสื่อสารทั้งหมดกับเซอร์วิสใน. Net โดยใช้โปรโตคอล JsonRpc

ตัวอย่างสำหรับคลาสในเซิร์ฟเวอร์:

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

การใช้รหัส TypeScript ที่สร้างขึ้น:

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

ตรวจสอบhttps://github.com/Rodris/TypedRpcสำหรับตัวอย่างวิธีการใช้งานอื่น ๆ


0

ASP.NET Web API Client Generatorsอาจมีประโยชน์มากกว่ามีค่าใช้จ่ายน้อยกว่า toolchain แบบผยองและอื่น ๆ ระหว่าง SDLC

ในขณะที่โปรแกรมเมอร์โดยทั่วไปใช้ WebApiClientGen เพื่อสร้างรหัสไคลเอ็นต์ API แต่โครงการนี้ยังมี POCO2TS.exe ซึ่งเป็นโปรแกรมบรรทัดคำสั่งที่สร้างอินเตอร์เฟส TypsScript จากคลาส POCO คุณสามารถใช้ Poco2ts.exe หรือคอมโพเนนต์ poco2ts เพื่อรวมการสร้างโค้ดเข้ากับบิลด์ไลน์ของคุณ


0

หากคุณต้องการแปลงผ่าน node.js คุณสามารถใช้แพ็คเกจนี้ (csharp-to-typescript) https://www.npmjs.com/package/csharp-to-typescript

sourcode: 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

หากคุณใช้ VSCode คุณสามารถดูส่วนขยายC # เป็น TypeScript ได้

แปลงคลาส C # เป็นอินเตอร์เฟส TypeScript

ดูว่าฉันสามารถแปลงจากรหัส C # ได้อย่างไรในคลิกเดียว แน่นอนว่าไม่ใช่ทุกคลาสที่จะถูกแปลงเช่นโรงงาน แต่ก็ดีพอสำหรับฝั่งไคลเอ็นต์ เราต้องการเพียงรูปร่างของข้อมูล บางครั้งถ้าฉันจำเป็นต้องใช้รูปแบบผู้เยี่ยมชมฉันจะตกแต่งด้วยวิธีการเพิ่มเติมที่ฉันต้องการ ใช้เวลาเพียง 5 วินาทีในการทำเช่นนั้น เมื่อเปรียบเทียบกับหนึ่งนาทีข้างต้นฉันสามารถพูดได้ว่ามันเป็นชัยชนะที่ยิ่งใหญ่

ดูรายละเอียดเพิ่มเติมในบล็อกโพสต์ของฉัน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.