Working on TypeScript 0.9: Generics, Overload on Constants and Compiler Performance

Working on TypeScript 0.9: Generics, Overload on Constants and Compiler Performance

Rate This
  • Comments 24

The upcoming TypeScript 0.9 release will represent the most significant changes to TypeScript since the first public release last October, bringing highly requested new language features as well as a significant re-design of the TypeScript compiler. In this post, we’ll give a first quick look at a few of the top investments for the 0.9 release.  Stay tuned for further updates in the coming weeks.

Generics

Generics has been the most highly requested feature over the last 6 month, and has always been something we planned to include in TypeScript 1.0.

To understand why this feature seems to be so important, it’s useful to first recognize that many common JavaScript APIs really are already generic. The simplest examples are the Array methods in the ECMAScript 5 core libraries. Take for example this use of the Array#map method:

    var lengthOfSecond = ["a", "bb", "ccc"].map(x => x.length)[1];


The ‘map’ method is generic – it can operate on any kind of array, and the kind of array it returns is based on the return type of the callback function it is passed.

Today, these relationships aren’t fully captured in the type of Array#map, and TypeScript infers the ‘lengthOfSecond’ variable above to be ‘any’. If these relationships were part of the type of the Array#map method, TypeScript would be able to infer that the type of ‘lengthOfSecond’ is ‘number’ in the example above without any further type annotation, leading to better tooling and static analysis without any extra work by the author of this code.

It turns out that arrays were special-cased in TypeScript 0.8 to enable part of this to be expressed, but that mechanism wasn’t made available to other libraries that do similar things, like underscore.js, d3, and others.

In TypeScript 0.9, generics will allow us to capture the true intended typing of these libraries. 

    interface Array<T> {
        // ...
        map<U>(callbackfn: (value: T, index: number, array: T[]) => U): U[];
// ...
}


Another example is knockout.js. Knockout has a notion of observables, which can be created over any type of value and used for databinding and change notification. A simple piece of code might look like:

    var person = {
        name: ko.observable("Bob"),
        age: ko.observabel(37)
    }

    var x = person.name().length + person.age();
    person.name("Robert");


Here ko.observable is a function which takes some type T and returns an Observable<T> which is a function that can be called with no arguments to get the value of type T, or called with one argument of type T to set the value. Without generics, we would have to say that the get and set functionality produce ‘any’. But with generics, we can capture the intended API shape:

    module ko {
        export interface Observable<T> {
            (): T;
            (value: T): any;
        }
 
 
       export function observable<T>(value: T): Observable<T>;
    }


These examples give a sense of what we expect generics to look like in TypeScript. Here’s a few other design principles that have guided the way we’ve been thinking about generics so far:

  • Generics, like the rest of the TypeScript type system, should be a compile-time only concept that disappears on compilation and generates no artifacts in the resulting JavaScript.
  • Classes and interfaces should be able to be generic.
  • References to the type without generic arguments should be the same as instantiating with ‘any’, so that ‘Array’ is the same as ‘Array<any>’.
  • Call and construct signatures in types should be able to be generic. Callsites can provide, or have inferred, the type arguments. Methods, functions and arrows and classes should all be able to describe generic signatures.

For full details, check out the current draft spec in the ‘develop’ branch on typescript.codeplex.com, and stop by the forums for any thoughts, feedback, questions or discussion.

Overload on Constants

A very common pattern in JavaScript APIs is to take a string parameter whose value implies something about the type that will be returned. Examples include the ‘createElement’ method in the DOM:

    var canv = document.createElement('canvas');
    canv.getContext('2d');


Because we passed the string ‘canvas’, this API will return a HTMLCanvasElement, which will allow us to call ‘getContext’. In TypeScript 0.8, the code above would have required an additional cast though, because the connection between ‘canvas’ and HTMLCanvasElement wasn’t available to the TypeScript compiler.

Overload on constants is a TypeScript 0.9 feature which allows functions taking strings to have additional overloaded signatures that take specific string constants as parameters, and may have more specific types elsewhere in the signature. For ‘createElement’, this might look like:

    interface Document {
        createElement(tagName: string): HTMLElement;
        createElement(tagName: 'canvas'): HTMLCanvasElement;
        createElement(tagName: 'div'): HTMLDivElement;
        createElement(tagName: 'span'): HTMLSpanElement;
        // + 100 more
    }


If you call this function with a string literal argument, you’ll get the more specific type. If you call it with a string expression other than a literal value (i.e. an identifier, the result of a function call, etc.) you’ll get the default ‘HTMLElement’.

Even straightforward cases like ‘createElement’ are fairly common in JavaScript, but there are some more interesting cases where this feature comes in handy. Take for example the ‘addEventListener’ pattern in DOM (or similar ‘on’ pattern in node.js):

    document.addEventListener('mousemove', ev => {
        ev.clientX;
    });


In TypeScript 0.8, the type of ‘ev’ would be inferred to be ‘Event’, and the code above would report an error because the ‘clientX’ property doesn’t exist on all ‘Event’s. But the ‘mousemove’ event will always provide a ‘MouseEvent’ argument, so there is an association between ‘mousemove’ as the first parameter to addEventListener, and the parameter type of the callback that is the second parameter to addEventListener.

    interface EventTarget {
        addEventListener(type: string, listener: (evt: Event) => void): void;
        addEventListener(type: 'mousemove', listener: (evt: MouseEvent) => void): void;
        addEventListener(type: 'mouseup', listener: (evt: MouseEvent) => void): void;
        addEventListener(type: 'blur', listener: (evt: FocusEvent) => void): void;
        // + 100 more
    }

 

Compiler Architecture

Along with the new language work, TypeScript 0.9 brings a new compiler architecture designed to better handle scaling up to the very large (100kloc and beyond) projects that we’ve seen TypeScript applied to over the last 6 months. To provide rich interactive tooling over projects of this size, the TypeScript compiler needs to be able to do only a relatively small amount of new work when a change is made to the source code.

To accomplish this, we’ve been working on a couple of foundational changes to the TypeScript compiler:

  • A new pull-based type checker which allows requests from IDE services like “what should I show in a completion list here” to do minimal incremental work.
  • A new parser which is more closely aligned with the spec’d TypeScript grammar.

This work is also helping with language correctness, bringing the compiler more fully in line with the language specification.

Path to TypeScript 0.9.0

Work is well underway on all of these features and many more for 0.9.0. Because this release will include a compiler with many significant changes, we’re hoping to get out an early alpha preview for early adopters to take for a spin in the next month or so. Until then, check out the spec drafts in the ‘develop’ branch, join us in the forums, and track progress at typescript.codeplex.com.

Leave a Comment
  • Please add 5 and 6 and type the answer here:
  • Post
  • excellent work!!!

  • Thank you very much for the update, I'm greatly looking forwards to the new release!

  • This is looking more and more interesting. Keep the awesome work!

  • I am loving TypeScript for my development.  This quote says it all.

    "For some programmers, static types are like annoying road signs which get in your way when you are trying to drive off-road into a ditch"

    Keep up the fantastic work.

  • Thanks for info about progres.

    Tried diffrent version, by using typescriptServices.js

    Latest LKG form develop branch does not work, latest version too. Infinite refreshing TS editor combos.

    Is there way to run it corectly, or is unworkable for now ?

  • I'd love to be an early adopter, if only for the improved performance. Completion has got really slow in my project.

  • @Mark and @Tristan

    The new 0.9.0 series language service will likely not work as a drop-in replacement while using the 0.8.3 Visual Studio plugin.  As Luke mentions, we're hoping to out an alpha for early adopters next month or so.

  • Awesome! Love where you guys are going with this. Generics will give a big boost in productivity!

  • @Jonathan

    Thanks for clarification. I thought that VS plugin is independent from typescriptServices.js.

    Why you use TypeScript to develop TypeScript compiler and language services ? By using C# or C++, you can get faster progress and higher performance. Then just generate JS version from these languages.

  • Any progress on EcmaScript 6 features like the spread operator and destructuring?

  • you have a typo in the typescript.codeplex.com link it links to http://typecript.codeplex.com/ ;)

  • This is just PERFECT. I'm itching to deploy it.

  • @jan

    Fixed.  Thanks!

    @Juan

    We're looking to bring in more ES6 features after the 1.0 release.

    @Tristan

    The language service is JavaScript-based so that it's easy to be used in web, desktop, and server settings.  Compiling to JS from languages that aren't natively similar to JS often gives you worse performance, not better.

  • May I ask you a little feature for this blog ?

    When I see an article on the homepage of the blog and I want to read the comments, I'm instinctively clicking on the phrase "13 comments" that's on the right of the header of each article; but that is not a link.

    Could you change the number of comments string into a proper link that points to the comments section of an article ?

    Thank you very much and best regards

  • Overload on Constants is exactly what TypeScript has been missing.  You guys are amazing :)

Page 1 of 2 (24 items) 12