68

Closed

Support optional ambient "this" pointer typing in callback/function signatures

description

Currently typescript will type the "this" pointer in function callbacks as "any." Arrow syntax lets us capture "this" from the outer scope, and this can enable proper typing. It would be nice to be able to provide
an optional "ambient this" declaration in function signatures:

(declare this : MyType, first: number, second: string) : any;

The rules would be:

1) the declaration is obviously optional
2) if specified, should be first parameter
3) cannot be used with arrow syntax lambdas (should be compiler error)

I know you can cast as needed to get the intellisense but I'd rather not put this responsibility on the implementer of the function. Ideally it should be part of the definition.

Example with Sammy:
var app = $.sammy("#view", function() {
     // define default route
     this.get("#/", function() { ... } 
});
In the above case, typescript can only type this as any.

Thoughts?
Closed Jul 28, 2014 at 10:17 PM by jonturner
As part of our move to GitHub, we're closing our CodePlex suggestions and asking that people move them to the GitHub issue tracker for further discussion. Some feature requests may already be active on GitHub, so please make sure to look for an existing issue before filing a new one.

You can find our GitHub issue tracker here:
https://github.com/microsoft/typeScript/issues

comments

LukeH wrote Nov 28, 2012 at 11:57 PM

@oisin: We've actually had discussions about a feature exactly like what you describe, and I believe it is a natural extension of current TypeScript which would enable use cases like the example you mention. Because 'this' is a keyword, we have actually talked about it as being written like:

var f: (this: MyType, first: number) => any;

And similarly, for implementations:

function f(this: MyType, first: number) { return 3; }

There's several unanswered questions around the precise rules for how this is treated in assignment compatibility, whether methods on classes have 'this' types, etc.

oisin wrote Dec 3, 2012 at 6:12 PM

Thanks Luke. It would interesting if you got a chance to update this thread with current thinking on implementation details.

WilliamMoy wrote Apr 11, 2013 at 5:21 AM

I think this is a very important feature. We have already seen several bugs where we used the wrong anonymous function syntax ("=>" vs "function"). Given that a lot of library (eg. jQuery) like to set the "this" refeerence of a callback, supplying the ability to provide this kind of typing in these libraries will help to catch the majority of these bugs at compile time.

Scriby wrote Apr 17, 2013 at 9:23 AM

+1

MgSam wrote Apr 25, 2013 at 5:46 PM

As you guys haven't mentioned it in the announcements, I'm assuming this feature is being pushed back from the 0.9 release?

jonturner wrote Apr 26, 2013 at 9:55 PM

@MgSam

Looks like it. We're focusing 1.0 on just making the compatibility story with JavaScript as strong as possible. This would go into making the TypeScript development experience nicer, which we're roughly thinking of 1.x and beyond.

MgSam wrote Apr 26, 2013 at 10:46 PM

This issue seems pretty important for compatibility with existing JavaScript, actually. I don't think you can have a really complete definition file for JQuery without this. I think to do JQuery really effectively you need: generics + overload on string constants + ability to set a type for the this pointer.

orefalo wrote Aug 22, 2013 at 11:32 AM

+1 working on typing meteor.js and this pattern is used all over the place. For now the user needs to cast this into a new variable - kind of ugly, but js is really a hack.

clausreinke wrote Aug 22, 2013 at 11:40 AM

jeffmay wrote Oct 25, 2013 at 9:43 PM

+1! It is a common pattern in a lot of JavaScript libraries to bind a given callback to some important object. Even if the declaration were ambient (ignored by the compiler when the bind method is called), it would be helpful and less messy than creating another "this dance"
function() {
    var dance: IComplexLibraryObject = this;  // not needed in JavaScript
    dance.complexSignature("", [])
}
Where the resulting JavaScript would be unnecessarily cluttered with:
function() {
    var dance = this;  // not needed in JavaScript
    dance.complexSignature("", [])
}

gerichhome wrote Jan 29, 2014 at 5:35 AM

It seems such rule should also work:
interface IDoSomething {
   test(first: number, second: string);

   x: number;
}

var d: IDoSomething = {
    test: (a, b) => {
        alert(this.x);       // OK
        alert(this.y);       // Error: y is not defined in IDoSomething 
    }
}

dhsto wrote Feb 3, 2014 at 5:20 AM

I think a neat way of defining this would be similar to c#'s extension methods:

function myFunction(this currentWindow: Window) {
alert(currentWindow.clientWidth);
alert(this.clientWidth); // identical to call above
}

myFunction(); // good
myFunction.call("string"); // bad, context is not the same type

dhsto wrote Feb 3, 2014 at 5:28 AM

Just to add what I said above... such a shame I can't edit comments to fix the formatting in my post... but this could be defined in the callback definition. Then when the user does the callback like, function() { } it will know what the type of this should be.

jamesnw wrote Mar 25, 2014 at 8:22 PM

I always end up creating my own variable as "_this" typed to what I need, but this would make things easier!

Bartvds wrote Apr 3, 2014 at 10:26 PM

I want this so much I accidentally posted a duplicate: https://typescript.codeplex.com/workitem/2364 (it should be closed).

It has some example cases (as if we need more? :)

GeoffArmstrong wrote Apr 24, 2014 at 11:27 PM

@LukeH: This could be used for the usual "context" parameter in things that take contexts, like map or foreach:
class Array<T> {
    map<TResult, TContext>(func: (this: TContext, item: T, index: number, array: T[]) => TResult, context: TContext) {
        var result = <TResult[]>[];
        for (var i = 0, len = this.length; i < len; i++) {
            result.push(func.call(context, this[i], i, this));
        }
        return result;
    }
}
That would be super useful! You could have proper Intellisense even without converting everything to arrow functions! Could you even verify that func.call's first parameter matches the "this" of its type? That would be even better!

abergs wrote Jul 24, 2014 at 11:05 AM