TypeScript, Knockout, TypeLite; strongly typed knockout development

TypeScript’s a great environment for development, and the strong typing is a real benefit, but in an MVC app it’d be good to integrate things between the server side and the client side. Let’s say you’ve got a data transfer object  defined in the server side, in C#, like so;

// C# Data Transfer Object
namespace TypeLiteDemo.ViewModels
{
  public class MyListItem
  {
    public int Id { get; set; }
    public string Title { get; set; }
  }
}

It would be great to request them from the server as JSON, and then on the client-side deal with it in a strongly-typed way with TypeScript. We want a matching declaration like this when dealing with objects from the server;

// TypeScript Data Transfer Object
declare module TypeLiteDemo.ViewModels {
  interface MyListItem {
    Id: number;
    Title: string;
  }
}

So that we can do this;

$.getJSON('MyListItems', function(data: TypeLiteDemo.ViewModels.MyListItem[]) {
  // do something with strongly-typed data from the server
});

Even better, it would be great, if you’re using knockout mapping, to have the mapping viewmodel version, too;

declare module TypeLiteDemo.ViewModels {
  interface MyListItemViewModel {
    Id: KnockoutObservable<number>;
    Title: KnockoutObservable<string>;
  }
}

So that you can do this;

$.getJSON('MyListItems', function(data: TypeLiteDemo.ViewModels.MyListItem[]) {
  // convert to knockout viewmodels;
  var vms = <TypeLiteDemo.ViewModels.MyListItemViewModel[]>ko.mapping(data);
  // now do something with the viewmodels;
  _.each(vms, function(vm) { /* extend the view models here */ }
});

Well, it should be no shock to learn that this blog post introduces a way of doing just that.

First, you’ll want to install TypeLite using NuGet. This gets you a default T4 template for generating the TypeScript definitions, but I’ll provide an alternative version below which generates the knockout viewmodels, too.

Copy and paste the code below into the TypeLite.tt file, modify the line that starts ‘var definitions’, and save. When you save, it’ll scan your assembly for the DTOs you mention, and produce two parallel sets of TypeScript defintions — one set for the DTOs, one for Knockout viewmodels.

<#@ template debug="false" hostspecific="True" language="C#" #>
<#@ assembly name="$(TargetDir)TypeLite.dll" #>
<#@ assembly name="$(TargetDir)TypeLite.Net4.dll" #>
<#@ assembly name="$(TargetDir)$(TargetFileName)" #>
<#@ import namespace="TypeLite" #> 
<#@ import namespace="TypeLite.Net4" #> 
<#@ import namespace="TypeLite.TsModels" #> 
<#@output extension=".d.ts"#>
<# 
 bool generateKnockoutFiles = true;
 var definitions = TypeScript.Definitions().For<TypeLiteDemo.ViewModels.MyListItem>();
#>
///<reference path='typings/knockout/knockout.d.ts' />
///<reference path='typings/knockout.mapping/knockout.mapping.d.ts' /><#= definitions #>
<# if (generateKnockoutFiles) { #>
<#= definitions
 .WithFormatter(KnockoutTypeConverter)
 .WithFormatter(KnockoutMemberTypeConverter)
 .WithFormatter(KnockoutMemberIdentifierConverter) #>
<# } #>
<#+
public string KnockoutTypeConverter(TsType type, ITsTypeFormatter formatter)
{
  return type.ClrType.Name + "ViewModel";
}

public string KnockoutMemberTypeConverter(string memberTypeName, bool isMemberCollection)
{ 
  if (isMemberCollection)
  {
    return string.Format("KnockoutObservableArray<{0}>", memberTypeName);
  }
  else
  {
    return string.Format("KnockoutObservable<{0}>", memberTypeName);
  }
}

public string KnockoutMemberIdentifierConverter(IMemberIdentifier identifier)
{
  return identifier.Name;
}
#>
Advertisements

4 thoughts on “TypeScript, Knockout, TypeLite; strongly typed knockout development

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s