139

I am just going to start use TypeScript in my HTML client project which belongs to a MVC project with a entity framework domain model already there. I want my two projects (client side and server side) totally separated as two teams will work on this... JSON and REST is used to communicate objects back and forth.

Of course, my domain objects on the client side should match the objects on the server side. In the past, I have normally done this manually. Is there a way to reuse my C# class definitions (specially of the POJO classes in my domain model) to create the corresponding classes in TypeScript"?

wonea
  • 4,783
  • 17
  • 86
  • 139
pabloelustondo
  • 2,196
  • 3
  • 19
  • 23
  • 10
    Yes I meant POCO. I know they are totally different languages... but it would be great to somehow avoid typing all this again... I think that for the moment I will copy and paste my c# code.. and will refactor anything it until it compiles in TypeScript... that way i am sure I did not type an attribute wrong.. and help me not having to remember. But this clearly does not sounds nice at all. – pabloelustondo Oct 18 '12 at 15:47
  • 1
    I'm also interested in the other direction - from Typescript model classes to C# model classes, bidirectionally deserializable through JSON. – Jason Kleban Jan 12 '13 at 11:43
  • I have been looking for the same and I have just stumbled upon [this Gulp plugin](https://github.com/ffMathy/gulp-typescript-cs-poco) although I don't know how much support there is for it – Luke T O'Brien Aug 08 '17 at 15:53

25 Answers25

58

There is not currently anything that will map C# to TypeScript. If you have a lot of POCOs or you think they might change often, you could create a converter - something simple along the lines of...

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

To

export class MyPoco {
    public Name: string;
}

There is also a discussion on Codeplex about auto-generating from C#.

Just to keep things updated, TypeLite can generate TypeScript interfaces from C#:

http://type.litesolutions.net/

Community
  • 1
  • 1
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • Yeahp, something like this is what i was thinking to do manually.. good. Not sure I have time to write a converter now.. but yes, that would be the idea. Thanks. May be Microsoft is just doing it right now whie we speak. – pabloelustondo Oct 19 '12 at 01:57
  • You could do this in any of two ways... Generate .ts files with help of EnvDTE Automation or by using Mef Directory Catalog and Reflection/Introspection. I prefer the second one as it does not depend on visual. – Arek Bal Oct 19 '12 at 07:54
  • 1
    I was starting to use dart and ran into the same issue of how to share types between c# and javascript. I hoped this problem would be solved by Typescript, but it looks like the answer is not yet. I was also a bit spoiled by the saltarelle-compiler which compiled c# to javascript rather nicely http://www.saltarelle-compiler.com/ – BraveNewMath Dec 17 '12 at 13:44
  • TypeLite does not work on enums, probably because they're not finalized until 0.9.0, which just hit beta. – Daniel Harvey Jun 17 '13 at 14:14
  • 2
    @DanielHarvey Enums are generated correctly by TypeLite, probably fixed since your comment. – pauloya Jul 08 '14 at 13:37
  • .. and TypeLite now has pretty good support for c# Generics too. – Simon Jan 22 '15 at 16:17
  • It would be nice to edit this answer and include WebEssentials from V.B.'s answer. For me it was the definitive solution. – villasv Apr 13 '16 at 14:18
  • @VillasV I'd prefer people up-vote the answers they feel are best rather than find myself stealing them all to put into my own. – Fenton Apr 13 '16 at 18:43
  • @Fenton , : How can I automatically add the export keyword to the generated interfaces? In the case of Enums I can see that Typelite automatically adds the “export” keyword and generates a nice export const enum BlaBlaEnum {} however in the case of classes this doesn't happen? How can I enforce the addition of export to the interfaces? – MHOOS Apr 07 '17 at 12:40
  • I have been looking for the same and I have just stumbled upon [this Gulp plugin](https://github.com/ffMathy/gulp-typescript-cs-poco) although I don't know how much support there is for it – Luke T O'Brien Aug 08 '17 at 15:50
  • 1
    TypeScriptPaste will convert C# intoTypescript. You have to have the Roslyn compiler, which means Visual Studio 2015, but it works really well. Its not 100% and I had to dumb down some code -- converting foreach into for loops, writing my own List class, for example, and you have to structure you code so its 'pure' with only internal data structures to work with, but it was a small price to pay for being able to modify and test code in VS and then 'compile' it to Javascript via TypeScript. – John Mott Oct 15 '17 at 19:16
39

Web Essentials allow to compile C# files to TypeScript .d.ts files on save. Then you could reference the definitions from your .ts files.

enter image description here

Robert MacLean
  • 38,975
  • 25
  • 98
  • 152
V.B.
  • 6,236
  • 1
  • 33
  • 56
  • Are you maybe confusing .ts files and c#? can't find this option – Flores Jul 21 '14 at 13:25
  • 1
    But after some searching I found what you mean.. actually quite useful. – Flores Jul 21 '14 at 20:43
  • 2
    Actually it isn't, I'm quite sure the operation 'compiles' nothing, it generates code. That confused me, but is't 99% correct, I'll give you that ;-) – Flores Jul 21 '14 at 21:10
  • 1
    It was more useful if we could config its namespace and some customization. – Farshid Saberi Dec 12 '15 at 18:49
  • 5
    You now have to install 'TypeScript Definition Generator' manually to use this - see [here](https://stackoverflow.com/a/45202853/1225497) – Taran Aug 23 '17 at 06:14
31

If you use vscode you can use my extension csharp2ts which does exactly that.

You just select the pasted C# code and run the Convert C# to TypeScript command from the command palette enter image description here A conversion example:

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

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

to

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

    /**Person name */
    Name : string;
}
Rafael
  • 2,642
  • 2
  • 24
  • 30
  • 3
    I think people are interested in a fully automated solution where compiling with msbuild outputs the typescript definitions prior to consuming them. Is there a way to get that using the plugin? – binki Mar 13 '18 at 18:52
  • No, this a VSCode plugin, only works inside the editor and not as a stand alone program – Rafael Mar 13 '18 at 18:57
  • I have convert this vscode extension into node module if you want through node.js or tytpescript. You can check the repo here: https://github.com/YuvrajSagarRana/csharp-to-typescript – Sagar Rana Magar Aug 25 '19 at 08:26
  • Is there a way to link command to keyboard shortcut? For example, would select .net code, and having setup shift+ctrl+c to execute "Convert C# to TypeScript", that would speed up the process. I like the plugin! – Pawel Cioch Jul 10 '20 at 15:00
27

TypeLite and T4TSs above both looked good, just picked one, TypeLite, forked it to get support for

  • ValueTypes,
  • Nullables
  • camelCasing (TypeScript root doc uses camels, and this goes too nice together with C#)
  • public fields (love clean and readable POCOs, also makes it easy for the C# Compiler)
  • disable module generation

Then I needed C# interfaces and thought it is time to bake my own thing and wrote a simple T4 script that just does what I need. It also includes Enums. No repo required, just < 100 lines of T4.

Usage
No library, no NuGet, just this plain simple T4 file - use "add item" in Visual Studio and choose any T4 template. Then paste this into the file. Adapt every line with "ACME" in it. For every C# class add a line

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

Order matters, any known type will be used in follwing interfaces. If you use only interfaces, the file extension can be .d.ts, for enums you need a .ts file, since a variable is instantiated.

Customisation
Hack the 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();
    }
#>

The next level of the script will be to create the service interface from the MVC JsonController class.

citykid
  • 9,916
  • 10
  • 55
  • 91
  • 1
    Very cute, but you have `typeof(ParticleKind)` in your `Enum`. Surely that should be `typeof(T)`? Also you need to update `bool` to be `boolean` for later TypeScript versions – iCollect.it Ltd Sep 13 '16 at 11:44
  • Also note: this breaks with enum properties in classes – iCollect.it Ltd Sep 13 '16 at 12:15
  • 1. Uhh, yes, thank you for the heads up, will fix that. 2. Typescript evolved since writing this script. Looks like it could need some update, you are certainly right. I hope it serves as a starting point for your own purpose. – citykid Sep 13 '16 at 13:42
  • 1
    I converted it into a helper library, fixed the bugs and now use just single-line entries in my TT files to generate interfaces and the new `const enum`s. Thanks for the example. It was very close and got me a working result much faster. – iCollect.it Ltd Sep 13 '16 at 13:45
20

Try Reinforced.Typings framework. Seems that it solves your problem.

  1. Install it from NuGet
  2. Navigate to your POCO and add [TsInterface] attribute above it

    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. Rebuild your project
  4. Find out generated TypeScript code in %Your_Project_Directory%/Scripts/project.ts file and add it to project manually

    module YourNamespace {
        export interface IYourPoco
        {
            YourNumber: number;
            YourString: string;
            YourArray: string[];
            YourDictionary: { [key: int]: any };
        }
    }
    
  5. Do the same for all your POCOs and reference project.ts in your other TypeScript code.

See more details in documentation wiki

Pavel B. Novikov
  • 948
  • 1
  • 9
  • 11
19

Here's my approach to solving it. Declare your C# classes with an attribute and .d.ts-files will be generated (using T4 transforms). There's a package on nuget and the source is available on github. I'm still working on the project, but the support is pretty extensive.

Christoffer
  • 2,125
  • 15
  • 21
19

If you're using Visual Studio, add the Typewriter extension.

Visual Studio Gallery

Website/Documentation

Update

With Web Essentials installed in VS 2015, you can right-click the class file, then > Web Essentials > Create Typescript Intellisense File from the context menu.

MikeT
  • 2,530
  • 25
  • 36
  • It created the d.ts file for intellisense support but how does one use it and then what is the relationship with creating the underlying classes? I went to the WebEssentails and can't find any docs. Any suggestions? TIA. – howardlo Jan 31 '17 at 20:28
  • @hio Just reference the d.ts file in whatever .ts file u need to use it in. When/if you change your class, the underlying d.ts file will update.(Tip: You can drag and drop the d.ts file to the top of a .ts file and it will create the reference for you.) – MikeT Feb 01 '17 at 02:45
  • Thanks Michael! I tried dropping the d.ts file to an empty .ts file and it only added the file to the containing folder. Tried it in VS2015 and the new VS2017, same results. Do you know of any video or tutorial that shows how this all works? TIA – howardlo Feb 08 '17 at 00:15
  • I tried dropping the d.ts file into a .ts file in the editor and it created the reference. I think I understand now. Tutorial still welcomed. Thanks! – howardlo Feb 08 '17 at 00:27
  • @hlo Sorry, don't know of any tutorials. When I go the Web Essentials route, I'v been making duplicate .ts files because my domain models are in a class library, and in my client side code, i usually need to create the dto or vm models, which the browser needs to know about. If you're having problems with all this, try the Typewriter extension. It allows you to have ur .ts model files anywhere you want. – MikeT Feb 09 '17 at 16:49
10

I have created a small utility that can generate TypeScript interfaces from C# classes. Is available as a NuGet package. Detailed documentation can be found on the project webpage.

Lukas Kabrt
  • 5,441
  • 4
  • 43
  • 58
  • Nice library, good effort. Unfortunately didn't find a way to tweak produced value type, e.g. convert "string" to "KnockoutObservableString" or "string[]" to "KnockoutObservableArray". Is it something that is in near future plans? – root Mar 27 '13 at 15:44
  • 2
    I used T4TS instead, it took 14 minutes. – root Apr 08 '13 at 15:47
  • @root, You shouldn't actually convert a string[] to KnockoutObservableArray in the generated definitions. You use these generated definitions when you're actually consuming the serialized instances of these classes. Now, you may be running the entire object through the mapping plugin, but I would caution against this. That can be excessive and I always prefer to *manually* convert (probably via a ctor) from your serialized instances of C# classes to TypeScript classes full of observables. The only time I'd suggest blindingly running it through the mapping plugin is for a CRUD style app. – Allen Rice Apr 09 '14 at 17:34
  • @Lukas Kabrt, thank you so much for TypeLite, it has been extremely beneficial in our projects and I wrote about it briefly here: http://www.underwatergorilladome.com/adopting-typescript-in-a-large-spa-project/ – Allen Rice Apr 09 '14 at 17:35
  • @AllenRice, I found it handy to have hand-crafted Knockout view-model classes, which is where I use the approach you desribed and combine it with a auto generated class which is a DTO that was passed through mapping plugin. – root Apr 10 '14 at 10:04
  • @root, yeah that's the approach I prefer, a hand crafted viewmodel. I just find that using the mapping plugin to map an entire DTO is excessive. BTW, here's a good Q/A about the mapping plugin and its use: http://stackoverflow.com/questions/7488208/am-i-overusing-the-knockout-mapping-plugin-by-always-using-it-to-do-my-viewmodel/ – Allen Rice Apr 10 '14 at 17:08
  • This is a great one and we have been using this for 3 years. However now we could not use it in new applications developed by .Net Core. `Is it supports .Net Core or any plan?` I could not able to find a one for .Net which generates on build of some assembly and puts a typings in a folder like TypeLite – Murali Murugesan May 12 '20 at 10:35
9

Please have a look at this library Typewriter

It converts not only classes, enums, interfaces etc, but also the api-controllers, which is simply awesome..

Plus it does as this as soon as you save the source .cs file, so I don't have to trigger any external tool. Save .cs and you get updated .ts

wonea
  • 4,783
  • 17
  • 86
  • 139
harishr
  • 17,807
  • 9
  • 78
  • 125
  • The great thing is that it produces TS files on saving corresponding CS files. However, setting the output folder is quite fragile (see [discussion here](https://github.com/frhagn/Typewriter/issues/106)) and requires some hand-crafted code. – Alex Klaus Sep 01 '17 at 03:54
  • I do use build tools for that, and this tool is way more powerful than most mentioned here – harishr Sep 01 '17 at 11:32
  • Only problem is when we are using containers, it become a dependency to have typewriter already installed in the container. – Shashank Sood Sep 14 '21 at 09:19
6

I have a little solution which uses T4 templates (view source).

You go from any CLR POCO:

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

To a TypeScript interface:

///<reference path="Child.d.ts" />
///<reference path="Person.d.ts" />
interface Parent extends Person {
    Name : string;
    IsGoodParent? : bool;
    Children : Child[];
}
diachedelic
  • 2,195
  • 1
  • 24
  • 28
4

If you need to create a separate file for every generated TypeScript class/interface (i.e. in a "single class per file" manner), you can try TypeGen. It's a tool you can use from the Package Manager Console to generate TypeScript files based on your C# classes/enums. It currently supports:

  • exporting enums; exporting POCOs as TS classes or interfaces
  • inheritance
  • generic types
  • collection / nested collection types

plus some additional features. It's also open source (you can check it out on github).

JB1
  • 41
  • 2
3

You can use the open-source project NSwag: In the GUI, you can select .NET class from an existing .NET DLL and generate the TypeScript interface for it.

The project also provides command line tools and support for T4 templates as well as generation of client code for Web API controllers...

Rico Suter
  • 11,548
  • 6
  • 67
  • 93
  • Be aware, that NSwag will generate TS files only either from the binary files (you need all dependencies of your binaries as well) or its JSON file, which Swagger produces from the binary files. – Alex Klaus Sep 01 '17 at 03:51
3

Guys have a look at https://github.com/reinforced/Reinforced.Typings. I have been playing with typelite and t4 templates for last few days and ended up with this project. It is super-simple and works like a charm. Just get the package, modify the configuration file (it is like for 10 seconds) and build. All gets done automatically without any issues. Bless the author!

The bad thing about T4 templates is that once you build from VS the scanned assemblies are locked and you must restart VS (how silly is that?). There are some workaround in T4 Toolbox + some VS cleaning directives but none of these worked for me.

veb
  • 145
  • 6
3

You can also use this: https://github.com/pankleks/TypeScriptBuilder

This small library generates TypeScript type definition based on C# types. Use it directly in your backend C# project to generate code for your frontend TypeScript project. You can also wrtie small console app, to generate code by pre-build tools.

Works on Full & NET Core framework!

Install by nuget: Install-Package TypeScriptBuilder

Supported features

  • Resolving type dependency
  • Generics
  • Type inheritance
  • Namespaces (modules)
  • Enums
  • Nullable types
  • Dictionary converison (to strong type TS indexed objects)
  • Set of code generation control attributes
  • any for types that can't be converted

More description: https://github.com/pankleks/TypeScriptBuilder/blob/master/README.md

pankleks
  • 693
  • 6
  • 14
2

I wrote a feature request about this on the Developer Community page:

https://developercommunity.visualstudio.com/idea/1153873/reuse-existing-net-classes-for-typescript-definiti.html

As you can see in the answers here there are many projects that tries to solve this but unfortunately many of these projects are single developer projects that stops being supported along the way. I think it would be really neat if Microsoft maintained one of these projects instead, creating a .NET to TypeScript generator.

Among others, some are still maintained but relies heavily on a single developer:

TypeLite:

https://bitbucket.org/LukasKabrt/typelite/src/default/

TypeScript Definition Generator:

https://marketplace.visualstudio.com/items?itemName=MadsKristensen.TypeScriptDefinitionGenerator

Typewriter:

https://github.com/frhagn/Typewriter

TypeGen:

https://github.com/jburzynski/TypeGen

Reinforced.Typings:

https://github.com/reinforced/Reinforced.Typings

My tip would be to choose a project that you from the start expect to stop being maintained and is prepared to switch to something that is currently maintained. I would go for something that uses annotations and cross my fingers I could replace simply that tag with something else if that project stops being supported.

Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • +1 I was worried about support too, but Reinforced.Typings is working well so far in my evaluation and it has been supported for over 7 years since 13/Sept/2015, so it's probably the best of the lot (I love how it integrates into the MSBuild process, so it works with both Visual Studio and Jetbrains Rider). – Eric Mutta Sep 21 '22 at 23:24
1

How about the other way around?

Check out erecruit TypeScript Translator. It comes with ready to go C# support, but is actually template-based (uses Nunjucks for rendering), which means it can generate anything else - VB.NET, F#, C++, XML, SQL - whatever you can encode with a template.

Works as a .NET console program, NodeJS program (for those not on Windows), or as a Visual Studio extension, complete with generate-on-save functionality. And includes MSBuild support, just to make your build server happy. :-)

Fyodor Soikin
  • 78,590
  • 9
  • 125
  • 172
1

I like the citykid solution. I had extend it a little bit. So, solution also is based on codegeneration technique with T4 templates.

It can generate common TypeScript types and ambient declarations.

It supports inheritance and interface implementations.

Supports generics, arrays and lists as type fields.

Also it translates to TypeScript types that doesn't explicitly mentioned in configuration (for example we import type A, and in TS output you can find some other types: types of the fields, base types and interfaces).

You can also override type's name.

Enums are also supported.

Usage example (you can find it in the project repository):

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

And you will get an output

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

Check full solution here: https://bitbucket.org/chandrush/cst4ts

RollingStone
  • 180
  • 1
  • 12
1

You can also use Bridge.net. From version 1.7 it supports generation of TypeScript definitions for C# types. See http://bridge.net/docs/generate-typescript-definitions/

George Birbilis
  • 2,782
  • 2
  • 33
  • 35
  • Main problem is when you do google search you may land at the original question and never notice that useful info existed at a duplicate/closed answer – George Birbilis Jun 01 '19 at 19:08
  • So make sure the canonical has the best information. Don't post to multiple questions. Any further discussion on this can be taken to [meta] – Martijn Pieters Jun 02 '19 at 18:12
1

I also liked @citykid's answer a lot, so I extended it to do a whole namespace at a time. Just put the POCO classes into the namespace, and rebuild T4 templates. I wish I know how to generate separate files for each, but that not the end of the world.

You need to reference the .DLL files in the top part (where the classes are that you want), and you need to mention the namespaces. All lines to edit are marked with ACME. Major cudos to @citykid, appreciate it!

<#@ 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
  • 2,353
  • 2
  • 28
  • 50
0

My solution was to write a small codegen util that simply takes a project assembly (and refering assemblies) and start scanning types that are involved in the interaction between typescript and c#. This util outputs both javascript as d.ts ... The tool is called in the post-build event ... works like a charm!

Paul0515
  • 23,515
  • 9
  • 32
  • 47
  • I dont know why I chose to output js and d.ts... right now it simply outputs .ts ... straightforward and very doable. – Paul0515 Oct 02 '13 at 22:30
  • 1
    Hi Paul...would you be able to share the util code with us. I am looking to just create a ts file (at a certain folder location) and not a d.ts file like you said. Is that something your util can do?? – Krishna Veeramachaneni Dec 30 '13 at 07:00
0

If interested, you can use TypedRpc. Its purpose is not only to create the interfaces in TypeScript, but to create all communication with the service in .Net using the JsonRpc protocol.

Example for a class in server:

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

Usage of generated TypeScript code:

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

Check out https://github.com/Rodris/TypedRpc for other examples of how to use it.

Rodris
  • 2,603
  • 1
  • 17
  • 25
0

ASP.NET Web API Client Generators may be more handy, less overhead than swagger toolchain and others during SDLC.

While programmers generally use WebApiClientGen to generate client API codes, this project also provides POCO2TS.exe, a command line program that generates TypsScript interfaces from POCO classes. You may use either Poco2ts.exe or the poco2ts component to integrate the code generation with your build pipeline.

ZZZ
  • 2,752
  • 2
  • 25
  • 37
0

If you want to convert it through node.js then you can use this package(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;
}
Sagar Rana Magar
  • 550
  • 1
  • 3
  • 18
0

If you are using VSCode, you could take a look at that extension C# to TypeScript

Convert C# class to TypeScript interface

See how I can convert it from C# code in just a click. Of course, not all the classes will be converted such as the factory, but it is good enough for the client-side. We just need the shape of data. Sometimes If I need to use the visitor pattern, I will decorate with additional methods that I need. It took only like 5 seconds to do so. Comparing with one minute above, I could say it is a big win.

See more detail in my blog post

trungk18
  • 19,744
  • 8
  • 48
  • 83
0

I use a small custom DSL that generates both TypeScript and C# DTO/POCO classes. The DSL looks like the following:

output CSharp
output TypeScript

namespace MyLibrary.Logic.DataModel.DTOs

class Something

property int ID
property string Name
property DateTime DateTime
property int ForeignKeyID

This way I have a DTO model that has a single source (the DSL) and sits between the two C# and TypeScript logic models.

I then write both a C# and TypeScript DTOFactory class that has methods responsible for converting between DTOs and Logic Model objects. (And part of the DTO generation are serialization methods that convert to an anonymous JSON-suitable type for serialization or for storing in IndexedDB.)

By doing this, as soon as there is a model change on either side, I get compilation errors until both sides match again.

Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80