Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Generating TypeScript from .NET Assemblies for Use with the Knockout.js Mapping Plug-in

0.00/5 (No votes)
15 Apr 2014 1  
The combination of C#, TypeScript, Knockout, and the mapping plug-in creates a problem. I need a tool to read my server-side C# classes and create client side TypeScript definitions

Introduction

Some parts of my Knockout ViewModels are objects that have been returned from my WebApi controllers and transformed by The Knockout mapping plug-in. So how can I get TypeScript type checking at design time when these objects are dynamically created observables?

The mapping plug-in takes JSON data and dynamically converts it to nested collections of Knockout observables. It's not going to write any TypeScript definition files for me.

This combination of C#, TypeScript, Knockout, and the mapping plug-in creates a problem. I need a tool to read my server-side C# classes and create corresponding client-side TypeScript definitions. But as an extra complication, they have to be Knockout observables, not normal TypeScript types.

Background

I found that there are some tools that will read the metadata from a .NET assembly and generate corresponding TypeScript definitions. But none of them solved the extra complication of generating Knockout observables.

I put together a basic console app that uses reflection to read an assembly and generate the TypeScript definition file for the data transfer objects (DTOs) I am returning to my Knockout ViewModel.

Using the Code

First, I wrote two custom attributes and added them to my project:

    public class TypeScriptKnockout
    {
        // use this attribute on C# classes for which you want to generate 
        // a TypeScript class with Knockout Observable properties
        public class GenerateTypeScript : Attribute
        {
            public override string ToString()
            {
                return "TypeScriptKnockout.GenerateTypeScript";
            }
        }

        public class NotObservable : Attribute
        {
            public override string ToString()
            {
                return "TypeScriptKnockout.NotObservable";
            }
        }         
    } 

Then I use the first attribute, GenerateTypeScript, on any classes I want to generate TypeScript for.

    [TypeScriptKnockout.GenerateTypeScript]
    public class MappingsViewModel : ISwisSecurityViewModel
    {
        public bool UpdateMode { get; set; }
        public string Version { get; set; }
        public string ServerName { get; set; }
        public string Environment { get; set; }

        public List<Profile> Profiles { get; set; }
        public Profile SelectedProfile { get; set; }

        public string ApplyToOtherEnvironment { get; set; }

        [TypeScriptKnockout.NotObservable]
        public GroupMappings GroupMappings { get; set; }
    } 

I use the second one, NotObservable, to indicate any properties that I do not want to be observable. This will sometimes be necessary, since the Knockout mapping plug-in only makes the "leaf nodes" of any data structure observable (so any classes that contain more classes are marked as "not observable").

I build my project and then run the command line tool. I specify my assembly name, and the namespace I wish to search for classes.

TypeScriptKnockout Security.dll Security.Models.ViewModels 

The tool reads the assembly, looks for classes and interfaces. It reads the properties and creates a TypeScript property with a matching observable type:

            switch (typeName)
            {
                case "Int32":
                    return notObservable ? "number" : "KnockoutObservable<number>";

                case "String":
                    return notObservable ? "string" : "KnockoutObservable<string>";

                case "DateTime":
                    return notObservable ? "Date" : "KnockoutObservable<Date>";

The generated types will be of KnockoutObservable<T>, which is defined in the type definition file for Knockout.js, available from Definitely Typed.

The console app generates a file with all the interfaces for the namespace you specify:

 class MappingsViewModel implements ISwisSecurityViewModel {
    UpdateMode: KnockoutObservable<boolean>;
    Version: KnockoutObservable<string>;
    ServerName: KnockoutObservable<string>;
    Environment: KnockoutObservable<string>;
    Profiles: KnockoutObservableArray<Profile>;
    SelectedProfile: KnockoutObservable<Profile>;
    ApplyToOtherEnvironment: KnockoutObservable<string>;
    GroupMappings: GroupMappings;
} 

Now include the generated definition file and you will get type checking at design time, and intellisense. As you can see, Visual Studio knows SelectedProfile is of type KnockoutObservable<Profile>

This console app method is pretty basic, and it would be nicer if it were called by the build script. But it works well enough.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here