Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal to improve static analysis for the thisArg of a function #1985

Closed
nathggns opened this issue Feb 9, 2015 · 54 comments
Closed

Proposal to improve static analysis for the thisArg of a function #1985

nathggns opened this issue Feb 9, 2015 · 54 comments
Labels
Duplicate An existing issue was already created Suggestion An idea for TypeScript

Comments

@nathggns
Copy link

nathggns commented Feb 9, 2015

This is an official proposal for one of the issues outlined in #229.

Firstly, I'd like to give a big thank you to @ahejlsberg for identifying the issue, and to @redexp, @ivogabe, @cspotcode, and others for their contributions to the original issue as they have formed most of the grunt work of this proposal. I've just decided I want the feature enough that I will take the time to write an official proposal.

I'd also like to note this is my first official proposal (for not just TypeScript, any language). I'm sorry if I've missed anything glaring out. Any advice on how to improve my proposals would be appreciated.

The Problem

When working with many libraries that take callbacks (including jQuery), TypeScript will give very little intelligence as to the type of the thisArg inside a callback's body �– intact, it will simply be typed as any.

A few examples follow

// A standard jQuery example. While jQuery does
// also provide the value of `thisArg` as a argument
// to the callback, it is still helpful to provide a familiar example 
$('body').on('click', function() {
    this // any?
});

// The example from the original issue
// There would be no intellisense in this function, and TypeScript will
// just assume everything is okay even if we misspell get or Sammy's API changes
// because this is typed to any. 
$.sammy('#view', function() {
    this.get('#/', function() { ... });
});

// Here is a typescript example
// myClickHandler obviously relies on its thisArg being a HTMLElement
// but nothing stops code from calling it with any thisArg either
// directly (myClickHandler(...)) or with call/bind (myClickHandler.call(...))
function myClickHandler(event : MouseEvent) {
    this.innerHTML = [event.pageX, event.pageY].join(', ')
}

document.body.addEventListener('click', myClickHandler);

Because the thisArg is of type any, we get no type checking and no autocomplete or any other kind of intellisense. Also, when defining functions within TypeScript user land code that rely on the thisArg being a certain type, there is nothing stopping consuming code calling it with whatever type they provide.

Proposed Change

Ideally, a function definition would be able to define the type of its thisArg. This means that any consuming code must provide a thisArg of this type (using call, apply, or bind). It also means that within the body of that function, we can assume that the type of its thisArg is of the provided type, rather than reverting to an any type.

Quick Note: None of this applies to functions defined using arrow functions. You cannot specify the type of the thisArg with arrow functions as their type is lexically bound. I believe if you try to use the syntax specified below with arrow functions it should be either a compiling error or a parsing error.

There are many different places where you can define a function in TypeScript. However, the main syntax is as follows at the moment.

function someFunction(someArg : someType) : someResult { ... }

I propose adding to this syntax a way of specifying the type of the thisArg within the function body.
There are a few suggestions for this syntax including some from the original issue and some I have thought of myself, but I think the best out of the suggested is the following syntax:

// Function Statement
function someFunction<this : SomeThisType>(someArg : SomeArgType) : SomeReturnType {
    // this is of type SomeThisType
}

// Function expression
var someFunction = function<this : SomeThisType>(someArg : SomeArgType) : SomeReturnType {
    // this is of type SomeThisType
}

// Function argument
// This would work similairly for declared functions (declare function ...) in definition files
function someFunction(someCallback : <this : SomeThisType>(someArg : SomeArgType) => SomeReturnType) : SomeReturnType { .. }

someFunction(function() {
    // this is of  type SomeThisType
});

// Interface function argument

interface SomeFunctionArgument {
    <this : SomeThisType>(someArg : SomeArgType) : SomeReturnType;
}

function someFunction(someCallback : SomeFunctionArgument) : SomeReturnType { ... }

someFunction(function() {
    // this is of  type SomeThisType
});

In the statement and expression examples, we can assume that the type of thisArg inside their bodies is SomeThisType. In the last two examples, we can assume that type type of thisArg inside the passed callbacks is of type SomeThisType.

However, these assumptions rely on enforcing that any TypeScript code calling these functions have to pass the correct thisArgs. Calling functions in JavaScript can get very complicated but here are the different ways that you can pass a thisArg, along with the semantics of this proposal.

Taken the following code:

class SomeType {
   doStuff() { ... }
}

var method = function<this : SomeType>() {
    this.doStuff();
};

We would no longer be able to call this function simply by invoking it, as the thisArg would be set to the global object instead of an instance of SomeType. Trying to do so should throw a TypeError of some description (the exact error is intentionally left undecided at this point).

method(); // Error: Window cannot be converted to SomeType ...

Specifying the type of thisArg using call, apply, or bind with an incompatible type will throw the same error. With call and apply the error is triggered at call time. With bind, it is triggered at "bind time" (to more easily find the source of an error).

method.call({});
method.apply({});
method.bind({})();

Only invoking with a compatible type of SomeType will be accepted by the type engine.

method.call(new SomeType());
method.apply(new SomeType());
method.bind(new SomeType())();

Again, I would like to stress that this proposal has no affect on arrow functions. Even attempting to specify the thisArg for arrow functions should be an error.

var someFunction = <this : SomeType>() => { ... }; // Error

One notably exception to this is that you are able to pass arrow functions as arguments where a thisArg has been specified.

Example:

declare function someFunction(someCallback : <this : SomeType>() => number);

someFunction(() => 1);

Note: bind does not change the specified thisArg of a function. This can only be set at define-time.

The Syntax

You will have noticed that we pass the thisArg within the type arguments of a function, where you would normally pass generic type information. This does not conflict however, as the thisArg must be the last type argument, separated by a , if any other arguments are passed, and must be prefixed with this: as follows.

class SomeType<T> {
    constructor(public someProp : T) {}
}

function someFunction<T, this : SomeType<T>>() : T {
    return this.someProp;
}

someFunction(); // Error
someFunction.call({}); // Error
someFunction.call(new SomeType<number>(1)); // 1

Note: This is a very contrived example, as very rarely will a function ever take a generic argument as well as specifying the thisArg as specifying the thisArg is usually done when declaring function arguments and then passing anonymous functions like so:

declare someFunction(someCallback : <this : SomeType>() => number) : number;

someFunction(function() {
    // this is of type SomeType
});

Why this syntax?

I chose this syntax as the main use case for this feature is for anonymous functions, in which you almost if not never have generic type arguments for. Alternatives were to add the this into the parameter list, which I disliked as it made for a messy-looking solution in the common use case.

declare function someFunction<this : SomeType>(someArg : SomeArgType): any;

// vs

declare function someFunction(this : SomeType | someArg : SomeArgType): any;

Personally, I find the latter much harder to read.

How is this problem solved?

With this change, you can now specify the thisArg within function bodies. While this works for any functions, the main use case is for passing anonymous functions to library code. With this change, you can now specify in a declaration the thisArg in the body of a function argument.

This is a very basic example of how the on function could be written in a jQuery definition file. I've put the definition and usage in one file for convenience.

Note how in this example, the whole semantics of enforcing the type of thisArg when calling a function is irrelevant as we're simply telling TypeScript how existing JavaScript works. This is actually the common use case for this code.

// Definition
interface jQueryStatic {
    (...any) : jQueryStatic; // Simple call definition. 
    on(eventName : string, callback : <this : HTMLElement>() => any) : jQueryStatic;
}

var $ : jQueryStatic;

// Usage

$('body').on('click', function() {
    // this is now of type HTMLElement
});

Void thisArg

In some cases, the value of thisArg can be undefined (directly invoking a function in strict mode). It can be helpful to disallow consuming code from being able to use the value of this even if it isn't undefined, by making TypeScript assume it is undefined.

You can do this like so:

declare function someFunction(arg : <this : void>() => void);

someFunction(function() {
    // this === undefined
});

Classes

Currently, explicitly setting the type of thisArg in class methods is unsupported (triggering a syntax error) as doing this comes with its own set of problems, and can be added on at a later date once the basic semantics have been sorted.

This also means you cannot dynamically change a method on a object to a function that has a specified thisArg. Also, to maintain currently functionality, TypeScript will continue to assume that this is of the type in which the method is defined within the method body, but will allow you to trigger it with any thisArg.

class Foo {
    someMethod() {
        // this is assumed to be of type Foo
    }
}

var foo = Foo();
var method = foo.someMethod;

// All okay
foo.someMethod();
someMethod.call(foo);
someMethod();
someMethod.call({});
someMethod.apply({});
someMethod.bind({})();

// Not okay

foo.someMethod = function<this : number>() { .. };

Automatically setting the thisArg within class methods

In the original issue, @cspotcode suggested that the thisArg automatically be set for class methods as such that the following is an error.

class SomeType {
    someMethod() { ... }    
}

(new SomeType()).someMethod.call({});

I have intentionally left this out of this proposal as it causes a lot of its own backwards incompatibility issues, hence why there is no syntax addressed for setting the thisArg for class methods. However, should this proposal be accepted, then it might be worth extending it to address this. Should you choose to do this, this comment should be of some help to you.

Interfaces

While specifying a thisArg type for function interfaces is allowed, trying to do the same for methods or properties is not. This is something that can be revisited if/when class method support for thisArg type specifications is added.

interface SomeFunctionInterface {
    <this : number>(): number;
}

declare function numberMap(arr : number[], transformer : SomeFunctionInterface) : number[]; // Okay

interface SomeOtherInterface {
    numberMapper : SomeFunctionInterface; // Error
}

Assignability

If B is assignable to A, function<this:B> is assignable to function<this:A>, but not vice versa.

function<this:any> is assignable to function<this:A> andfunctionthis:B.functionthis:Aandfunctionthis:Bis assignable tofunctionthis:any`.

class A {}
class B extends A {}
var x = function<this : number>(){};
var y = function<this : string>(){};
var z = function<this : A>(){};
var zz = function<this : B>(){};
var a = function<this : any>(){};

x = y; // Error
y = x; // Error
zz = z; // Error

z = zz; // Okay
x = a; // Okay
y = a; // Okay
z = a; // Okay
zz = a; // Okay

a = x; // Okay
a = y; // Okay
a = z; // Okay
a = zz; // Okay

Updating lib.d.ts

@cspotcode also showed that updating lib.d.ts to properly set the thisArg could cause a lot of breakage. Again, to simplify this proposal, I have left this out. Again, this is something I think should be addressed after choosing to merge this proposal, and this feature is still useful without updating lib.d.ts.

Emitted JavaScript

Regardless of the syntax used to specify the thisArg, it must be stripped from the outputted JavaScript. This is simply a syntactical addition to the type engine, which is always stripped from emitted javascript.

Incompatibilities

I won't claim to know of every feature currently proposed for ES6 and ES7 / current TypeScript proposals, but I don't believe that the proposed syntax conflicts any proposed feature.

Breaking Changes

There are no breaking changes by simply adding this feature (as functions must manually add an annotation for the type of their thisArg). Breaking changes would only be introduced should this be added to lib.d.ts, or if classes begin to automatically set their thisArg.

@Arnavion
Copy link
Contributor

Arnavion commented Feb 9, 2015

  1. When you say that method.bind({})(); (where method has thisArg of type SomeType) is an error, is it an error at the call to bind or the call to the bound function? I guess it's the former.

  2. If type Foo is assignable to type Bar, is function<this: Foo>() assignable to function<this: Bar>() ? Is function<this: Bar>() assignable to function<this: Foo>() ? I guess the first is no and the second is yes.

  3. Is function<this: any>() assignable to function<this: Foo>() ? Is function<this: Foo>() assignable to function<this: any>() ? I guess both are yes (this follows from the previous point, since all types are assignable to any and any is assignable to all types).

  4. Is any function without an explicit thisArg considered to have a thisArg of type any ? (I think this would maintain current behavior.) Do fat arrow functions fall in this category? What about class methods, since you say they don't have an implicit thisArg that's the type of the class?

  5. Is it allowed for the thisArg to be void ? This would cover the case where a function takes a callback and calls it without providing a particular this value. In unstrict mode this inside the callback would be the global object, and in strict mode it would be undefined, so it would be nice to be able to say a callback parameter has <this: void> so that this cannot be accidentally used inside the callback argument.

  6. Does the result of bind automatically get a thisArg type? Say:

    var foo: Foo;
    var bar: () => void;
    var baz = bar.bind(foo);

    Is baz of type function(): void or function<this: Foo>(): void ? If the latter, this might introduce the same breaking changes that you're trying to avoid by not having an implicit thisArg on class methods, so I guess it's the former.

  7. You have an example of an interface method with a thisArg type that's not the same as the interface. This can be easily implemented by object literals, but how would a class implement such an interface? That is, are class methods also allowed to have a thisArg type that's not the same as the class type? Or is it simply impossible for a class type to implement such an interface. If the former, then such a method would have to be disallowed to be called on an instance of the class:

    interface Foo {
        foo: number;
    }
    
    class Bar {
        bar: string;
    
        someMethod<this: Foo>() { return this.foo; }
    }
    
    var barInstance = new Bar();
    barInstance.someMethod(); // Error?
    
    class Baz extends Bar {
        foo = 5;
    }
    
    var bazInstance = new Baz();
    bazInstance.someMethod(); // Valid?
  8. Is this allowed?

    class Foo {
        foo: number;
        someMethod() { return this.foo; }
    }
    
    interface Bar {
        bar: string;
    }
    
    var foo = new Foo();
    foo.someMethod = function<this: Bar>() { return this.bar.length; } // Error?
    
    foo.someMethod(); // Direct call to redefined someMethod(). Error?
    
    (function (x: Foo) { x.someMethod(); })(foo); // Indirect call to redefined someMethod(). Error?

    Since someMethod doesn't have an explicit thisArg nor an implicit thisArg per your proposal, it would seem it's allowed.

@nathggns
Copy link
Author

nathggns commented Feb 9, 2015

@Arnavion Thanks for identifying some of the issues with my proposal.

Here are my solutions:

  1. While it would be nice to make method.bind({})() error at call time (for consistency with other possible errors introduced by this proposal, I feel it could be confusing and hard to find the source of this error if we did do that. For that reason, it should fail when calling bind.

  2. When specifying a desired type for the thisArg, passed thisArgs must be either of that type or a subtype of that.

    Example:

    class Foo {}
    class Bar {}
    class FooBar extends Foo {}
    
    declare function someFunction(someCallback : <this : Foo>() => void);
    
    someFunction.call(new Foo()); // okay
    someFunction.call(new FooBar()); // okay
    someFunction.call(new Bar()); // error
  3. Yes, for the reasons you said.

  4. Yes. Fat arrows do also fall in to this category. Class methods are a bit of both. From outside, they have their thisArg specified as any inherently (it's actually a syntax error to try to set it explicitly as class methods are outside of the scope of this proposal). However, inside the body of a class method, the thisArg is assumed to be the type of the class in which the method is defined. This is to maintain current functionality.

    class Foo {
       someMethod() {
           // this is assumed to be of type Foo
       }
    
       // syntax error as doing this is outside the code of this proposal
       // to simplify a lot of things 
       // Can look at adding support for this once the proposal is accepted
       someOtherMethod<this : number[]>() {
    
       }
    }
    
    var foo = new Foo();
    var method = foo.someMethod();
    foo.someMethod(); // Allowed
    method(); // Allowed
    method.call({}); // Allowed
    method.apply({}); // Allowed
    method.bind({})(); // Allowed

    Note: If I haven't explained this well enough, let me know. I'll try again.

  5. Yes. TypeScript should assume that this === undefined inside the body of function<this : void>s.

  6. The type of thisArg can only be set at definition time. Using bind has no effect on this as long as you're binding to a compatible type. Trying to bind to an incompatible type is an error as explained in the proposal.

  7. This is not actually an interface method, it's a interface defining a function. Interface methods are not within the scope of this proposal as class methods are not within this proposal.

  8. Your first call with fail. The second won't, as it expects x to be of type Foo, so x.someMethod has an inherent type of function<this : any> (though code inside its body will assume that it is of type Foo, as explained in point 4.

I hope this answers some of your questions.

@nathggns
Copy link
Author

nathggns commented Feb 9, 2015

I would just like to say, for me at least, adding new syntax that further conflicts with React's JSX would be a bad idea. If the proposed syntax conflicts with the implementation of jsx-typescript over at https://github.com/fdecampredon/jsx-typescript/ then I think the syntax should change.

(Look at facebook/react#759 for more info on TypeScript/JSX compatibly).

@jasonmeisel
Copy link

If the proposed syntax conflicts with JSX (which it seems like it would, but this should be verified), here are some other possibilities:

  1. declare function someFunction(this : SomeType, someArg : SomeArgType): any;
  2. declare function someFunction((this : SomeType), someArg : SomeArgType): any;
  3. declare function someFunction(this : SomeType)(someArg : SomeArgType): any;
  4. declare function someFunction(SomeType)(someArg : SomeArgType): any;
  5. declare function someFunction : SomeType (someArg : SomeArgType): any;

The biggest advantages of the proposed syntax are:

  • It won't conflict with any other syntax rules, since right now angle brackets cannot be in a function signature at all
  • It's clear to readers that the type is declared for "this" without prior knowledge of the syntax (unlike 4 and 5 above)

For those reasons, I think 3 is the best replacement for the proposed syntax.

I would also like to mention that I think this is a very important feature for the future of TypeScript, as many JS libraries rely on this-binding. The specific library where I found this problematic was @meteor-typescript.

@cspotcode
Copy link

@jasonmeisel Your first advantage is incorrect, as angle brackets can be in a function signature. Angle brackets are used for TypeScript generics.

Example: (view in TypeScript playground)

class AbstractBuzz {
    foo: number;
}
class BuzzImpl extends AbstractBuzz {
    bar: string;
}
class Wrapper<T> {
    constructor(public wrapped: T) {}
}
function wrapIt<T extends AbstractBuzz>(instanceToWrap: T): Wrapper<T> {
    return new Wrapper(instanceToWrap);
}

var wrappedBuzz = wrapIt(new BuzzImpl());
console.log(wrappedBuzz.wrapped.bar); // Compiles correctly thanks to generics.

EDIT: A much better example of generic function signatures are the underscore / LoDash type definitions: https://github.com/borisyankov/DefinitelyTyped/blob/master/lodash/lodash.d.ts

@nathggns
Copy link
Author

nathggns commented Feb 9, 2015

For more examples of generics, look at the declarations for Q.js

// Some trivial example. Would never do this
var nums = Q.Promise<number[]>(resolve => {
    resolve([1, 2, 3]);
});

nums.then(nums => {
    nums // typescript knows this is a number[]
});

@nathggns
Copy link
Author

nathggns commented Feb 9, 2015

@jasonmeisel The guys over at React feel this wouldn't conflict with JSX anymore than TypeScript already does, and wouldn't break their current implementation. I propose we stick with the originally suggested syntax of function<this : Type> for this reason.

facebook/react#759 (comment)

@jasonmeisel
Copy link

@cspotcode @nathggns My mistake, I forgot about function generics :) If the proposed syntax doesn't break any existing systems, I am all for it!

@Arnavion
Copy link
Contributor

Arnavion commented Feb 9, 2015

@nathggns

1.For that reason, it should fail when calling bind.

I do agree it should fail when calling bind. I just wanted to confirm that was your intent.

2.When specifying a desired type for the thisArg, passed thisArgs must be either of that type or a subtype of that.

That is not what I asked. I asked about the assignability of a function type with a particular thisArg to another function type with a different thisArg based on the assignability of the type of the thisArgs.

7.This is not actually an interface method, it's a interface defining a function. Interface methods are not within the scope of this proposal as class methods are not within this proposal.

Sorry, I misread your example. That said, by allowing them on the function operator you're also allowing them on interface and class methods. Consider:

interface FooMethod {
    <this: Foo>(x: string): number;
}

interface Bar {
    func: FooMethod; // Equivalent to   func<this: Foo>(x: string): number;
}

Do you intend that func's thisArg type should revert to any in this case?

8.Your first call with fail. The second won't, as it expects x to be of type Foo, so x.someMethod has an inherent type of function<this : any> (though code inside its body will assume that it is of type Foo, as explained in point 4.

Can you clarify what you mean by "first call" ? There are three lines in my example - the assignment, the direct call on the instance, and the indirect call through the class type. Which of these would compile with your proposal and which wouldn't?

If by "first call" you mean the foo.someMethod() line, then this is surprising. I don't expect any difference between the two method calls (the second and third lines), as a difference there would require foo to stop being of type Foo, which TS does not support. So either all three lines should compile, or the assignment (the first line) should not compile and the other two lines become irrelevant.

@nathggns
Copy link
Author

That is not what I asked. I asked about the assignability of a function type with a particular thisArg to another function type with a different thisArg based on the assignability of the type of the thisArgs.

Sorry do you mean the following?

 class A {}
 class B extends A {}
var x = function<this : number>(){};
var y = function<this : string>(){};
var z = function<this : A>(){};
var zz = function<this : B>(){};

x = y; // Error
y = x; // Error
zz = z; // Error
z = zz; // Okay

Do you intend that func's thisArg type should revert to any in this case?

I think trying to set a property of an interface to a function interface that has a specified thisArg should be an error at this point, in order to avoid undefined behaviour. Eventually, the intention is to support explicitly setting the thisArg for class and interface methods but that is outside of the scope of this initial proposal, so I would make it an error for now.

Can you clarify what you mean by "first call" ? There are three lines in my example - the assignment, the direct call on the instance, and the indirect call through the class type. Which of these would compile with your proposal and which wouldn't?

Sorry I was being short-sited here. Yes, the assignment should error in order to stop changing Foo.

class Foo {
    someMethod() {

    }
}

var foo = new Foo();

foo.someMethod = function<this : number>() {}; // Error

foo.someMethod(); // Wouldn't even reach this. 

@Arnavion
Copy link
Contributor

Thanks, that answers my questions. You could put these points into your proposal post.

@nathggns
Copy link
Author

@Arnavion I have done that now.

Can I ask, do you support this proposal? While I know it would help me massively, and possibly the guys over at @DefinitelyTyped, I'm not sure how much other people need this feature.

@Arnavion
Copy link
Contributor

I have been subscribed to #229 for a long time, so yes I do support it :)

this-typing is very helpful for callbacks, of course, but my own use for this feature would be to annotate interface methods with <this: void> (see #1545 (comment)) and to see how implementing such an interface would look like. I know your proposal specifically excludes this-typing on interface methods, but it's a starting step.

@aholmes
Copy link

aholmes commented Feb 10, 2015

@nathggns

I'm not sure how much other people need this feature.

For what it's worth, I'm only an average TypeScript developer and constantly litter my code with var self = <MyType>this; just to get "this" type support. I think it's really greatly needed, especially with any prototypal inheritance being done. In my opinion it's a blindingly obvious hole in the language.

@nathggns
Copy link
Author

Thank you. Would've been a bit rubbish to have written the proposal and for no one to really want it.

Sent from my iPhone

On 10 Feb 2015, at 03:19, Aaron Holmes notifications@github.com wrote:

@nathggns

I'm not sure how much other people need this feature.

For what it's worth, I'm only an average TypeScript developer and constantly litter my code with var self = this; just to get "this" type support. I think it's really greatly needed, especially with any prototypal inheritance being done. In my opinion it's a blindingly obvious hole in the language.


Reply to this email directly or view it on GitHub.

@cspotcode
Copy link

If you want a quick idea for how the language should deal with a particular situation, you can rewrite the code so that this is a function argument. Then see what the compiler does.

For example, to test how this situation should behave:

function dogIsPet<this: Dog>(): boolean { return this.name && this.collar; }
function animalIsPet<this: Animal>(): boolean { return this.name; }
dogIsPet = animalIsPet; // is this allowed?
animalIsPet = dogIsPet; // is this allowed?

Rewrite <this: T> into (thisArg: T) and compile:

function dogIsPet(thisArg: Dog): boolean { return thisArg.name && thisArg.collar; }
function animalIsPet(thisArg: Animal): boolean { return thisArg.name; }
dogIsPet = animalIsPet; // is this allowed?
animalIsPet = dogIsPet; // is this allowed?

@QueueHammer
Copy link

Love this, elegant and non breaking.

@thanzen
Copy link

thanzen commented Feb 20, 2015

+1

@DanielRosenwasser DanielRosenwasser added the Suggestion An idea for TypeScript label Feb 21, 2015
@danielearwicker
Copy link

Don't know if this is implied but I'd like the other arguments to Function.call to be type checked. At the moment this is allowed:

declare function f(num: number);
f.call(null, "bad");

because call is not treated specially by the language - it's just a method declared (I guess in lib.d.ts) with variable arguments of type any. Restricting the type of the this-arg would be a half-measure if all the other arguments were still untyped at the call site.

(And perhaps apply could be typed so it accepts a tuple with the right types.)

As these would be breaking changes, perhaps they could be "switched on" by the presence of the this-arg type proposed here.

@saschanaz
Copy link
Contributor

@danielearwicker You mean this #212, right? :D

@aholmes
Copy link

aholmes commented Mar 6, 2015

It is a little strange this proposal addresses typing for thisArg but not for subsequent arguments in call, et al.

Has anyone taken a stab at patching this proposal into the compiler? I can't imagine @danielearwicker's addition would be overly complex to add.

@Artazor
Copy link
Contributor

Artazor commented Mar 6, 2015

Eagerly awaiting this feature!

@danielearwicker
Copy link

I've been thinking about this (and this) and it seems to me the key point is that this proposal introduces a new type, an "unbound method", which is not actually a type of function. You cannot call it directly with (). It has methods of its own, which you can call, including bind from which you can obtain a function that can be directly called. From the proposal:

function someFunction<T, this : SomeType<T>>() : T {
    return this.someProp;
}

someFunction(); // Error

And for me the pressing problem is catching the accidental creation and erroneous use of unbound methods, through isolated references to properties. I may have:

function makeCounter(init: number) {
    return {
        next() {
            return init++;
        }
    };
}

var c = makeCounter(5);
var five = c.next();
var next = c.next;
var six = next();

This is allowed in TS today, and happens to be correct. But what if makeCounter is changed to:

function makeCounter(init: number) {
    return {
        c: init;
        next() {
            return this.c++;
        }
    };
}

Now it is erroneous to grab a reference to c.next and later call it. I think this is a highly visible, easy-to-encounter issue in TS today. Users coming from C# are most likely surprised by it (as a C# delegate created from a method group is sensibly automatically bound). It makes classes especially fragile and error-prone. Right now in the TypeScript playground, newbie user/troublemaker has heard that functions are first class, and so adds:

var g = greeter.greet;
alert(g());

When they run it, it says "Hello undefined". With classes under the present TS type system, we are always a stone's throw away from this kind of error and the compiler ignores it.

Also (whatever Crockford is saying this year!) there are realistic justifications for introducing the use of this as code evolves, and TS/ES6+ encourages it with the convenience of class (maybe broadened in the future by #497). Future ES runtimes are likely to get ever-better at optimising object layout in memory where they have static information about the common features of objects, provided through shared prototypes containing methods, or explicit classes where ES6+ is directly supported by the runtime.

So I think as well as allowing explicit declaration of these unbound-method types, TS should assume an unbound-method type is being created whenever a function-type property is accessed without being immediately called (with "immediately" having the same meaning as it does for ES's behaviour with automatically binding this).

Although it would cause backward incompatibility, it would most likely be welcomed. At any time in a large codebase a team member may want to introduce a use of this. Better for a codebase to be prepared for this by using bind/call wherever it might be necessary. It sucks that ES classes are so bad that they impose a "bind tax" on codebases that don't even use this, but here we are. We live in a this world, and TS has to be this-friendly by nature.

Therefore, even if the coder never deliberately uses this proposal's syntax, I'm arguing that it would be valuable if this proposal had a backward-incompatible impact:

var c = makeCounter(5);
var five = c.next();
var next = c.next; // next is an unbound method
var sixBad = next(); // disallowed - an unbound method is not a function
var nextBad: (() => number) = c.next; // disallowed, same reason
var boundNext = next.bind(c);
var six = boundNext(); // allowed

How could we mitigate this backward-incompatibility in situations where the coder is Crockford and wants to avoid this and classes altogether? It's possible that the compiler could put information into the type returned by makeCounter, clarifying whether next refers to this or not. This would reduce the backward-incompatibility impact on code that avoids this. However it's not quite that simple, because classes implement interfaces and interfaces don't currently carry that information, e.g.

interface Counter {
    next(): number;
}

In the above proposal it is stated: "While specifying a thisArg type for function interfaces is allowed, trying to do the same for methods or properties is not." This makes perfect sense, because next already requires a this of type Counter. So:

var c: Counter;
var n = c.next; // an unbound method
n(); // disallowed
n.call(c) // allowed

So if this needed to be addressed, what actually appears to be needed is a way to state that a function property is not dependent on this. Where the compiler can see the actual type of an object (e.g. it's from an object literal or class where the compiler can see if methods depend on this) it could automatically preserve that information.

Straw-man syntax:

interface Counter {
    function next(): number;
}

By prefixing with function we declare that the method implementation cannot depend on contextual this. Consequently consumers of this type can grab c.next and later call through it without worrying about binding. And for example, an ordinary class method may not implement next().

So in related issue #229 @RyanCavanaugh gave us some food for thought (using a different syntax from this proposal):

class MyClass {
    x = 5;
    fn1 = () => { console.log(this.x); }
    fn2() { console.log(this.x); }
}

declare function callme(f: (this: SomeType));

var m = new MyClass();
callme(m.fn1); // Allowed? Not allowed?
callme(m.fn2); // Allowed? Not allowed?
callme(window.focus); // Allowed? Not allowed?

The first might be allowed because fn1 ignores the contextual this and is always bound via closure captured-this. Might be better to disallow it through.

The second should only be allowed if SomeType structurally satisfies MyClass, because callme isn't going to be able to call through f without providing an instance of SomeType via bind/call.

The third isn't allowed if we assume window.focus requires this to be a Window, not a SomeType (is there some reason why we couldn't assume that? If it is always bound a la fn1, it could be declared as such in lib.d.ts using the function prefix.)

Supposing the compiler is "smart" re: the function prefix idea, and extracts the interface of MyClass as:

interface MyClass {
    x: number;
    function fn1(): void;
    fn2(): void;
}

Then:

 var m = new MyClass();
 var fn1 = m.fn1; // plain function
 fn1(); // allowed
 var fn2 = m.fn2; // unbound method
 fn2(); // disallowed

@nathggns
Copy link
Author

nathggns commented Mar 8, 2015

I think for the most part that that is an entirely separate proposal to this and should be submitted as so. I purposefully ignored automatically binding the thisArg on class methods as it would be a breaking change, whereas this proposal is not and could easily be introduced in a 1.x update.

Sent from my iPhone

On 8 Mar 2015, at 13:46, Daniel Earwicker notifications@github.com wrote:

I've been thinking about this (and this) and it seems to me the key point is that this proposal introduces a new type, an "unbound method", which is not actually a type of function. You cannot call it directly with (). It has methods of its own, which you can call, including bind from which you can obtain a function that can be directly called. From the proposal:

function someFunction<T, this : SomeType>() : T {
return this.someProp;
}

someFunction(); // Error
And for me the pressing problem is catching the accidental creation and erroneous use of unbound methods, through isolated references to properties. I may have:

function makeCounter(init: number) {
return {
next() {
return init++;
}
};
}

var c = makeCounter(5);
var five = c.next();
var next = c.next;
var six = next();
This is allowed in TS today, and happens to be correct. But what if makeCounter is changed to:

function makeCounter(init: number) {
return {
c: init;
next() {
return this.c++;
}
};
}
Now it is erroneous to grab a reference to c.next and later call it. I think this is a highly visible, easy-to-encounter issue in TS today. Users coming from C# are most likely surprised by it (as a C# delegate created from a method group is sensibly automatically bound). It makes classes especially fragile and error-prone. Right now in the TypeScript playground, newbie user/troublemaker has heard that functions are first class, and so adds:

var g = greeter.greet;
alert(g());
When they run it, it says "Hello undefined". With classes under the present TS type system, we are always a stone's throw away from this kind of error and the compiler ignores it.

Also (whatever Crockford is saying this year!) there are realistic justifications for introducing the use of this as code evolves, and TS/ES6+ encourages it with the convenience of class (maybe broadened in the future by #497). Future ES runtimes are likely to get ever-better at optimising object layout in memory where they have static information about the common features of objects, provided through shared prototypes containing methods, or explicit classes where ES6+ is directly supported by the runtime.

So I think as well as allowing explicit declaration of these unbound-method types, TS should assume an unbound-method type is being created whenever a function-type property is accessed without being immediately called (with "immediately" having the same meaning as it does for ES's behaviour with automatically binding this).

Although it would cause backward incompatibility, it would most likely be welcomed. At any time in a large codebase a team member may want to introduce a use of this. Better for a codebase to be prepared for this by using bind/call wherever it might be necessary. It sucks that ES classes are so bad that they impose a "bind tax" on codebases that don't even use this, but here we are. We live in a this world, and TS has to be this-friendly by nature.

Therefore, even if the coder never deliberately uses this proposal's syntax, I'm arguing that it would be valuable if this proposal had a backward-incompatible impact:

var c = makeCounter(5);
var five = c.next();
var next = c.next; // next is an unbound method
var sixBad = next(); // disallowed - an unbound method is not a function
var nextBad: (() => number) = c.next; // disallowed, same reason
var boundNext = next.bind(c);
var six = boundNext(); // allowed
How could we mitigate this backward-incompatibility in situations where the coder is Crockford and wants to avoid this and classes altogether? It's possible that the compiler could put information into the type returned by makeCounter, clarifying whether next refers to this or not. This would reduce the backward-incompatibility impact on code that avoids this. However it's not quite that simple, because classes implement interfaces and interfaces don't currently carry that information, e.g.

interface Counter {
next(): number;
}
In the above proposal it is stated: "While specifying a thisArg type for function interfaces is allowed, trying to do the same for methods or properties is not." This makes perfect sense, because next already requires a this of type Counter. So:

var c: Counter;
var n = c.next; // an unbound method
n(); // disallowed
n.call(c) // allowed
So if this needed to be addressed, what actually appears to be needed is a way to state that a function property is not dependent on this. Where the compiler can see the actual type of an object (e.g. it's from an object literal or class where the compiler can see if methods depend on this) it could automatically preserve that information.

Straw-man syntax:

interface Counter {
function next(): number;
}
By prefixing with function we declare that the method implementation cannot depend on contextual this. Consequently consumers of this type can grab c.next and later call through it without worrying about binding. And for example, an ordinary class method may not implement next().

So in related issue #229 @RyanCavanaugh gave us some food for thought (using a different syntax from this proposal):

class MyClass {
x = 5;
fn1 = () => { console.log(this.x); }
fn2() { console.log(this.x); }
}

declare function callme(f: (this: SomeType));

var m = new MyClass();
callme(m.fn1); // Allowed? Not allowed?
callme(m.fn2); // Allowed? Not allowed?
callme(window.focus); // Allowed? Not allowed?
The first might be allowed because fn1 ignores the contextual this and is always bound via closure captured-this. Might be better to disallow it through.

The second is allowed because callme isn't going to be able to call through f without providing an instance of SomeType via bind/call.

The third isn't allowed if we assume window.focus requires this to be a Window, not a SomeType (is there some reason why we couldn't assume that? If it is always bound a la fn1, it could be declared as such in lib.d.ts using the function prefix.)

Supposing the compiler is "smart" re: the function prefix idea, and extracts the interface of MyClass as:

interface MyClass {
x: number;
function fn1(): void;
fn2(): void;
}
Then:

var m = new MyClass();
var fn1 = m.fn1; // plain function
fn1(); // allowed
var fn2 = m.fn2; // unbound method
fn2(); // disallowed

Reply to this email directly or view it on GitHub.

@danielearwicker
Copy link

@nathggns Agreed. They are almost orthogonal in that you propose extending the syntax and not introducing incompatibility, whereas the core of what I'm suggesting is deliberate (focused) backward incompatibility, any new syntax being an optional extra.

@nathggns
Copy link
Author

nathggns commented Mar 8, 2015

I think that my proposal (which could be introduced in 1.6?) would pave the way to make it possible to easily implement your proposal in 2.

Sent from my iPhone

On 8 Mar 2015, at 14:56, Daniel Earwicker notifications@github.com wrote:

@nathggns Agreed. They are almost orthogonal in that you propose extending the syntax and not introducing incompatibility, whereas the core of what I'm suggesting is deliberate (focused) backward incompatibility, any new syntax being an optional extra.


Reply to this email directly or view it on GitHub.

@danielearwicker
Copy link

@cspotcode:

Prefixing methods with function is a clever idea. It's equivalent to saying this: any in the function signature.

I described it above as: "By prefixing with function we declare that the method implementation cannot depend on contextual this."

i.e. the rule is that function must not refer to this at all, and it can be policed by the compiler, so that client code can safely treat such functions as first class values without having to manually bind them, etc. It's not another way to say this: any. That would indeed be pointless.

My suggestion was probably off-topic (see subsequent comments above). The original issue is only about stating the type of this (which is what you appear to be interested in, so you are on-topic!).

I drifted onto a suggestion that TS should automatically assume function properties require a this value, and so not allow them to be called without this-context. I suggest it is advantageous for a future version of TS to be incompatible with programs that try to directly call things that may not actually be callable in that way, so it can catch more errors than it presently can.

Then I added the suggestion that, to allow us to conveniently avoid the whole problem by not using classes and this, it should be possible to declare that a function property does not require this, so is callable without this-context. The orientation of defaults is dictated by the (unfortunate) popularity of classes. An interface, as written today in TS, should still be implementable by a class.

It should not be allowed. m.fn2, must be invoked with a this value assignable to type MyClass.

You're right, I hadn't spotted there were two types involved. NB. SomeType may be structurally compatible with MyClass, in which case it would be allowed! :) I've updated my comment accordingly. Thanks for reading my entire comment so carefully!

Does the TypeScript compiler have any special-case code to handle function.prototype.bind,

No, and I believe current opinion is not keen on changing this (there have been a few issues requesting that they be strongly typed).

@danielearwicker
Copy link

In fact, my function prefix on a named interface member could be read as equivalent to <this: void> in the example from the original issue.

@danielearwicker
Copy link

Just noticed that in the proposal, it says:

We would no longer be able to call this function simply by invoking it, as the thisArg would be set to the global object instead of an instance of SomeType.

In "use strict" mode, this would be undefined.

@cspotcode
Copy link

I see what you mean about the compiler enforcing that a function's implementation never uses this. That's very useful. I was more focused on another situation, which you also addressed:

...declare that a function property does not require this, so is callable without this-context.

For that case, specifying this: any in an interface indicates that the function can be invoked with any this binding because the implementation doesn't care; it doesn't use this.

If I understand correctly, we want two separate type-checks for this. Within the implementation of a function, we want this: void, which disallows us from accidentally doing stuff like this.foo() From the perspective of external code calling that same function, we want this: any so that the calling code has no restrictions on the this value it provides.

What if we used this: {}|void? In the function implementation, we're prevented from doing this.foo, and outside the function, calling code is allowed to pass anything for this. (unlike void, which only lets you pass null or undefined)

this: {}|void even works for strict mode functions where this can be undefined.

TypeScript can implicitly set this: any for arrow functions and functions whose body doesn't reference this. It already does for arguments that are never referenced.


Does the TypeScript compiler have any special-case code to handle function.prototype.bind,

No, and I believe current opinion is not keen on changing this (there have been a few issues requesting that they be strongly typed).

Oh ok.

I wonder if it makes sense to add some useful typing to .bind without becoming restrictive. For example, we don't need to enforce the proper type of bound arguments. We can continue to allow

(function(a: number) {}).bind(whatever, 'a string is not a number')();

However, if bind is passed a function that requires a certain this binding, it can return an identical function that does not require a particular this binding (since, obviously, bind has just pre-bound the this value)

var foo = function(this: Window, a: number) {}); // (this: Window, a: number) => void
var foo2 = foo.bind(window); // (this: any, a: number) => void

Does it make sense for the type of this to be a generic on the Function interface?

interface Function<ThisType> {
    bind(thisArg: ThisType, ...args: any[]): (...any[]): any;
    apply(thisArg: ThisType, args: any[]): any;
    // ... 
}

@danielearwicker
Copy link

@cspotcode - I'd prefer if the compiler pointed out to me that I'm passing a this value that is in fact going to be ignored, as it probably means I'm doing the wrong thing inadvertently. Unfortunately the case where a function doesn't use this is very much the easy half of the problem. My hope was that TS could move to a situation where every function had a specific this type (which could be void), and this could usefully happen automagically from existing declarations. But then I realised it's a pretty hopeless idea, which is why I haven't created a separate issue.

@teppeis
Copy link

teppeis commented Mar 27, 2015

FYI: Flowtype has quite similar syntax (angle brackets and colon separator) for _bounded type parameters_, instead of thisArg.

Flow | Bounded Polymorphism

class BagOfBones<T: Bone> { ... }
function eat<T: Food>(meal: T): Indigestion<T> { ... }

@Arnavion
Copy link
Contributor

@teppeis The equivalent TS for that is T extends Bone

@cspotcode
Copy link

@teppeis: Typescript already has that same syntax for generics, which are the same as bounded type parameters. Both are different than specifying the type of a variable, argument, or property (this is a special argument). That's why several of us prefer not to use the angle bracket syntax for specifying the type of this: angle brackets are for generics, whereas postfix type annotations are for specifying the type of variables.

Generics and bounded type parameters let you specify generic types constrained to other types. Contrast that with : someType, which let you constrain variables to types. (where those types might be generic types) This thread is concerned with constraining the type of this, which is a variable, not a generic type.

@wizofaus
Copy link

My vote is for the first/specially named parameter syntax. Anyone who has declared a function in the existing language with a first argument called 'this' deserves what they get really!

E.g. setTimeout should be declared as
setTimeout(callback: (this: Window, ...args: any[]) => void, timeout?: number, ...args: any[]): number;

Rather than just use "any" for callback as it does now.
I would still hope to get a warning if I did this though:
class Test {
string name;
foo() {
setTimeout(function() {
document.writeln(this.name);
}, 10);
}
}

Because it's more likely I was thinking 'this' referred to the current class rather than the global Window object (which also has a 'name' property).

@saschanaz
Copy link
Contributor

@wizofaus Your syntax confuses me and makes me think the callback literally receives this object as its parameter. I think this is a problem.

@wizofaus
Copy link

I would say "this" has enough semantics attached to it for any programmer that that's a pretty unlikely interpretation. I suppose an alternative is something like the extensions syntax used by C#:

setTimeout(callback: (window: this Window, ...args: any[]) => void, timeout?: number, ...args: any[]): number;

But it seems silly to have to name the parameter (and with Extension methods in C# you don't use "this" inside the function body).

TBH if it were entirely up to me I'd actually simply change the meaning of 'this' to match that of C#, and introduce a different term for the cases where you need the existing semantics inside a function (though I'm not quite sure what I'd call it, and frankly I'd rather always use a named parameter).

@cspotcode
Copy link

Yeah, the callback literally does "receive" this object as a parameter. That's how it works for all functions. It just happens that there are different rules for how you pass the value of this, it doesn't appear in the arguments array, and there's a different default value in non-strict mode. Otherwise it's basically just another argument.

Plus, TypeScript intends to add a type system onto vanilla ECMAScript, which means not covering up JS semantics. All TypeScript developers must understand how this works, for better or worse.

As a reminder, syntax is not at all preventing the Typescript team from implementing this feature. They still need a complete spec that addresses the specific semantic issues @RyanCavanaugh has mentioned.

@wizofaus
Copy link

"TypeScript intends to add a type system onto vanilla ECMAScript, which means not covering up JS semantics"
Why does it mean that? TypeScript is, to me, just a "better" language based on JavaScript, that happens to compile to JavaScript. If it continue to propagate things that make JavaScript difficult to code well with then it's failed in being an improvement.
Understood about the complete spec - my main concern is still some sort of "smart" detection that warns when it appears you're probably misusing "this", i.e. when the method/field you're referring to after "this." is one in the current class. Even with full typing for 'this' such a mistake is possible.

@cspotcode
Copy link

I'm just saying that, realistically, proposals that stray too far from the TS developers' goals are likely to be rejected. They want to align with ECMAScript 6, adding a type system and tooling on top of Javascript, allowing easy interop with existing Javascript libraries. They intentionally avoid overly-complex code transformations. It's fairly easy to migrate existing codebases to TypeScript because they've focused on a type system and tooling, not on changing language semantics. Even the current arrow functions and classes compile into ES6 arrow functions and classes.

If the goal is merely a far better language, one might argue you should write C# and compile that into JS.

@Artazor
Copy link
Contributor

Artazor commented Apr 16, 2015

For syntax I'd propose

declare class MyClass {
    someMethod(x: integer);
}

var x = function[MyClass](x: integer) {
   // this: MyClass
   this.someMethod(x);
}

And for generics:

declare class OtherClass<T> {
    someMethod(arg: T);
}

var y = function<T>[OtherClass<T>](x: T) {
    // this: OtherClass<T>
    this.someMethod(x);
}

About type system (roughly): this type should be strictly co-variated since we can not assign to this. And that is why in TS it can't be an additional parameter.

@kitsonk
Copy link
Contributor

kitsonk commented Jun 8, 2015

Sorry for arriving late at this, as the biggest "item" is the ability to do essentially late binding of typing to allow chaining. I have seen a fair few machinations, but maybe we are missing something that would not impede compatibility with JavaScript/EcmaScript and would be essentially very much TypeScript without challenging generic usages. Admittedly, I am not at all familiar with the sort of hoops that this "late" typing would have on the compiler, but what about the following syntax:

interface A {
    foo(): typeof this;
}

class B implements A {
    foo(): typeof this {
        return this;
    }
}

class C extends B {
    bar(a: typeof this): typeof this {
        return a.foo();
    }
}

let c = new C();
c.foo().bar(c).foo();  /* should be totally valid */

So essentially, instead of a version of generics, or something else, whenever you need to type something of the current class (or validate that something is implemented an interface properly) you would use typeof this.

While, I haven't worked through all the emitting of the above, I think it could easily be determined and that the "reserved" type of typeof this would always be determined within the context of block it was existing in, meaning it can be scoped properly.

@pmccloghrylaing
Copy link

@kitsonk I think that suggestion belongs in #285.

My vote is for this as the first argument, without a special separator. It seems to more accurately represent what is happening without adding a special syntax. I think by including this : any implicitly for functions that don't specify this means assignability is already taken care of as this is treated as another argument. It should also make things easier to implement. Unfortunately this does make assignability looser than it could be (see: Function Argument Bivariance).

@nathggns: your proposal has assignability the wrong way around for z and zz:

class A {}
class B extends A {}
var z = function<this : A>(){};
var zz = function<this : B>(){};

z.call(new A()); // Okay
z.call(new B()); // Okay
zz = z // Okay

zz.call(new A()); // Error
zz.call(new B()); // Okay
z = zz; // Error

@jussi-kalliokoski
Copy link

FWIW, I (unaware of this issue) opened a similar one on facebook/flow#452, in my last comment proposing the same syntax that was proposed here (e.g. function add (this : number, b: number) { return this + b; }).

I think that would be the most intuitive syntax for this, and I use it in trine for annotating function types (I use _this because babel currently considers this as an illegal parameter name). This would nicely also leave room for something like a named this syntax which would make trine's paradigm more readable (and also give full control over the scoping and destructuring of this):

function add (&a : number, b : number) { return a + b; }
function vec2add (&[x1, y1], [x2, y2]) { return [ x1 + x2, y1 + y2 ]; }
// bind syntax:
[1,2]::add([5,3]) // [6, 5]

@Artazor
Copy link
Contributor

Artazor commented Aug 3, 2015

With respect to the new ES7 "::" bind operator I'd propose

var abc = function MyClass::(x: integer) {
   // this: MyClass
   this.someMethod(x);
}

function MyClass::abc(x: integer) {
   // this: MyClass
   this.someMethod(x);
}

var abc = function<T> OtherClass<T>::(x: T) {
    // this: OtherClass<T>
    this.someMethod(x);
}

function<T> OtherClass<T>::abc(x: T) {
    // this: OtherClass<T>
    this.someMethod(x);
}

Will it lead to the breaking changes?

@Artazor
Copy link
Contributor

Artazor commented Aug 3, 2015

It will work also with {} types:

function {count:number}::inc() {
     this.count++
}

The most difficult seems to parse the case where type should be enclosed into parentheses:

function (()=>void)::inc() { ... }
function (A | B)::test() { ... }

And even

function<T> (T::(x: number)=>number)::bar(y: number, t: T): number {
     return t::this(y) + y
}

var foo = function {factor:number}::(x: number) { return this.factor*x*x; }
console.log(foo::bar(5, {factor: 0.1})) // 7.5

However it seems that here we need not more than two or three look-ahead tokens. Need we?

@nathggns
Copy link
Author

nathggns commented Aug 6, 2015

Is there any official progress/comments on this?

@danquirk
Copy link
Member

danquirk commented Aug 6, 2015

@nathggns see #3694 for the latest proposal

@mhegazy
Copy link
Contributor

mhegazy commented Sep 16, 2015

this is now tracked by #3694

@mhegazy mhegazy closed this as completed Sep 16, 2015
@mhegazy mhegazy added the Duplicate An existing issue was already created label Sep 16, 2015
@Zorgatone
Copy link

+1

@microsoft microsoft locked and limited conversation to collaborators Jun 18, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Duplicate An existing issue was already created Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests