TypeScript for the C# Developer: TypeScript Generics

What if I told you that your JavaScript code could benefit from generics; just use it the same way as in C#? You’d call me crazy; you might even be tempted to cry “Witchcraft” or “Magic” or some other derivative of Arthur C. Clarke’s well-known quote:

Any sufficiently advanced technology is indistinguishable from magic—Arthur C. Clarke

If you’re using TypeScript, however, this is not magic. In fact, it’s very real, and was one of the initial goals of TypeScript along with its type safety system.

If you’ve used generics in C#, you’ve almost certainly at some point defined a function like the following:

public T addFunc<T>(T arg1, T arg2)
{
}

In C#, this defines a function whose type in not known until usage time. The <T> parameter allows us to tell the C# compiler what “T” represents when we use it. Here’s an example:

int result = addFunc<int>(2, 4);

In TypeScript, the same thing is also possible:

private addFunc<T>(arg1: T, arg2: T): T {
}

The syntax is almost the same as that of its C# counterpart, and it works in exactly the same way.

let result: number = addFunc<number>(2, 4);

The limitations are very much the same, too. You can’t, for example, define a “T” type and then try to access properties such as “.length” (only available on an array) or “toLocaleString” (only available on a number), because these are not guaranteed to exist on every available type.

Nevertheless, you can use them to great effect with custom interfaces and classes, to define complex types, and generic functionality.

In many of the TypeScript projects I work on, for example, I often use them to define functions for my Ajax network operations, to enforce the types I send of the wire from browser to service and back again, so I can be sure that I can use a single point of call for save & load operations, while still maintaining the ability to strongly type multiple object types.

One of the best uses of generics in TypeScript, however, is when dealing with the humble array.

In plain old JavaScript, you can define an array quite simply by doing the following:

let myArray = [];

This array has NO type information, and can hold ANYTHING you give to it. Here’s how:

myArray.push(123);
myArray.push("Hello World");
myArray.push(false);

This would result in an array, with a number, string, and Boolean in the first three parts.

If we wanted to, we could push ANY of the built-in types into it, and any custom types we might define. This is not at all a good place to be.

When using generics, we can define our array like so:

let myArray:Array<number> = [];

If we now try our multiple variable type trick again:

The compiler flagging the offending code with a red squiggly line
Figure 1: The compiler flagging the offending code with a red squiggly line

We can see immediately that the compiler flags those lines, not pushing a number with a big red squiggly error line.

You even can define and push complex types and classes onto the array in a type safe and structured manner.

There’s much more we can do here, too. The same generic treatment is available with the very new ES6 map types, and other enforced collections, such as those used for byte arrays in OpenGL graphics. Unfortunately, there is not time or space to delve into using those in this article.

Like C#, generics also extend to being used on Class definitions themselves, for example:

export class DataHandler<T> {

   private theData: T = null;

   constructor(initialData: T)
   {
      this.theData = initialData;
   }

   public operationOne(): T
   {
      return this.theData;
   }

   public operationTwo(anArgument: number): T {
      // after using the arg in some way
      return this.theData;
   }

   public operationThree(extraData: T)
   {
      // Do something using extra data and
      // this.theData
   }
}

If you combine this with interfaces and custom classes, you easily can build up things like Linq-based C# collections, for instance:

export class List<T> {

   private theData: Array<T> = null;

   constructor(initialData: Array<T>)
   {
      this.theData = initialData;
   }

   public add(arg: T)
   {
      this.theData.push(arg);
   }

   public remove(): T
   {
      return this.theData.pop();
   }

   // and so on.......

}

As of the date of this article being written, there are a number of JavaScript libraries available that are TypeScript friendly, and implement many of the C# collections, generics, and other such functionality that C# developers have gotten used to over the years.

There’s a ton more functionality here to be explored, too; we’ve only just scratched the surface. We can, for example, define an interface that has a given property in it, and then we can tell our generic function to only accept variants of T that implement that interface; for example:

interface HasLengthAvailable {
   length: number;
}

function myGenericFunction<T extends
      HasLengthAvailable>(arg: T) {
   // We know for a fact that .length is
   // present at this point
   console.log(arg.length);
}

If we then attempted to use that function with, say, a plain old “number” type, the compiler will reject it because “number” has no property called “.length”. An array or hash-map, however, would be perfectly fine, because those do have a “.length” property.

Remember, though, all of this is ONLY available inside the TypeScript language environment. Once it’s compiled to pure JavaScript, the output will behave like pure JavaScript, so you can’t define all of this good stuff, and then use your Library from within regular JavaScript projects.

I hope this brief introduction to some of the features TypeScript makes available in JavaScript for the C# developer has learned (sic) you a thing or two.

There’s much, much more to be explored, and if you’re a back-end .NET developer who’s looking to make the move into the brave new world of front-end code, TypeScript is the vehicle for you.

If you’ve not yet read the previous two parts in this miniseries, I highly recommend going back and reading them here:

Here’s a link to the official TypeScript documentation.

if you want to go beyond what we’ve covered here, I would strongly suggest having a read through it.

Many new frameworks are starting to come out that fully embrace working entirely in TypeScript. For example, there’s Aurelia, which has command line tools that will build you a first class, ready to run TypeScript application framework, ready to use all of the modern day JavaScript features, now enabled by ES6 standards.

The ability to create an entire application in the browser, while still using familiar concepts learned as an OOP developer in C#, is a very powerful thing, and we’re only just starting to see where this path can take us.

Shawty

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read