-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Support specifying type of "this" in function implementations #229
Comments
There are many JS libraries out there that depend on using a specifically bound "this" in callbacks. Note the codeplex issue had 68 votes. |
👍 |
2 similar comments
👍 |
👍 |
@fdecampredon Have you moved the issue regarding exposing private types? That issue is still important for Facebook React compatibility, right? |
Not yet @abergs I'll do it |
👍 |
+Needs Proposal (see https://github.com/Microsoft/TypeScript/wiki/Writing-Good-Design-Proposals) The major design impediment we had here was that it was really unclear what the subtype relationships are for functions with a declared Some food for thought: 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? |
I like "this" for a couple of reasons. Currently there is no way to create practical private values in classes using TypeScript syntax. When the situation requires private members in a class, read "function constructed object", I end up using regular JavaScript syntax and use "this" to create my public members. However as an "any" there is no auto complete of values assigned to "this" in the class, nor can any sub value of an "any" have a type. Being able to define "this" as an interface would mitigate this. Or we could allow private members in classes... but those comments are for a different issue thread. |
What if declare function action() {
this:SomeClass;
// ...
} |
@redexp I think it's better to have
How could you write that with |
Ok, but as for me it's kinda confusing when function callFunction(callback: ()[this:string] => void) {
callback.call('foo');
}
function zooKeeper(cage: AnimalCage)[this:SomeClass];
interface Widget {
sprock()[this:SomeClass]: void;
} |
|
Maybe as type parameter? interface Widget {
sprock<this:SomeClass>(): void;
} Would fit because it matches conceptually with how regular type parameters are specified and doesn't need to introduce another syntax pattern. I'm not sure if |
Perhaps we can use a left-side type parameter for specifying
|
@aholmes I think that would be fairly confusing for people coming from many other C-style languages like C#, Java, D, etc. Even I read that and assume that |
That's a fair point, though given that the return type follows the signature, maybe this is something to simply get used to. For what it's worth, I come from the C-style languages, and the ": type" syntax still confuses me, especially when working with JavaScript's object definition syntax. |
I like @Bartvds suggestion - fits well I think. |
@Bartvds I find it strange to add the |
How about to separate function show (this: Node | seconds: Number, callback: Function) {} |
@redexp That's very concise. That syntax could even be used for classes.
Great idea. |
I'm not so keen on having the type of To take @redexp example:
Myself, I find this more readable:
My reason is that when you look at the parameters you don't have to remember to harvest out the |
Guys, we extremely need it. All my code is in red because IDE don't know the type of |
@basarat @nycdotnet I agree with you guys. |
Consider also the case where you can specify a callback and the context it will be called with. For example, an EventEmitter3 event handler: emitter.on('somevent', function () {
// in this context 'this' is the same as the outer scope 'this' due to my third param to `.on()`
// TS treats 'this' here as 'any', but I want it to be a specific type that I can specify with an annotation
// without having to do a function bind or a self scope var.
}, this); |
Hey guys. Looks like we will never figure out how statically (I mean .d.ts files) type function getOnMethodThisType(on) {
if (on.arguments.length === 3) {
return on.arguments[2].type;
}
return TS.globalScope.type;
} Thinking about "code instead of config", maybe we need new definition file type where instead of pseudo code config will be just js/ts code? For example TS.class('Emitter')
.method('on', function (on: TSMethod) {
if (!on.callee) {
throw new TS.Error.CalleeRequired();
}
switch (on.arguments.length) {
case 2:
return {
"arguments": [String, Function],
"this": TS.global.type,
"return": on.callee.type
};
case 3:
return {
"arguments": [String, Function, Any],
"this": on.arguments[2].type,
"return": on.callee.type
};
}
throw new TS.Error.WrongArguments();
}); Pros: modular code, share code through npm, custom errors like "One argument is deprecated since v1.4", code is more readable than |
👍 |
👍 ... right now I'm using the "self workaround", and I'm not enjoying it Dx |
I agree with @ivogabe at #229 (comment). If it is a type parameter, that means the type of 'this' can vary on a per-call basis. And if it varies, then a 'this' expression will not have a known type in the body. Better to have it be a parameter with a type annotation. |
👍 |
FWIW, this is required for a lot of .d.ts files on DefinitelyTyped. Here's an incomplete list of modules that require this:
Also, many of the native ECMAScript APIs use bound // Array generics
interface Callback<T, U> {
(this: T, value: U, index: number, array: ArrayLike<U>): any;
}
interface ArrayLike<T> {
[key: number]: T;
length: number;
}
interface Array<T> {
// things...
forEach(this: ArrayLike<T>, callback: Callback<ArrayLike<T>, T>): void;
forEach<U>(this: ArrayLike<T>, callback: Callback<U, T>, thisArg: U): void;
// more things...
}
// JSON
interface JSONCallback {
(this: any[], key: number, value: any) => any;
(this: {[key: string]: any}, key: string, value: any) => any;
}
interface JSON {
parse(text: string, reviver?: JSONCallback): any;
stringify(value: any, reviver?: JSONCallback | string[], space?: string | number): string;
}
// Function methods
interface Function {
bind<T, U>(thisArg: T, ...args: any[]): (this: T, ...args: any[]) => U;
} So, definitely 👍 |
+1 Very need this feature. |
This is now tracked by #3694. |
+1 |
As an easy workaround, you can declare a variable at the top of the function, give it a type, and set it equal to
|
My work around for the last 2 years has been to just not use TypeScript. Though it's was for other issues at the time, which some of them have been fixed since v0.8. |
@QueueHammer thanks man, but not terribly constructive. Did you find an alternative that does fix this problem, and how? |
This is fixed by #6739 on April 8. |
But isn't released yet (it is in |
Has this now been released as part of 2.0? Is there a set of reference examples of using this? |
Yes. It is sort of covered here: https://www.typescriptlang.org/docs/handbook/functions.html But taking an example from above: myMethod(this: MyClass) {
this.doStuff();
} |
Migrated issue from codeplex: https://typescript.codeplex.com/workitem/507
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:
The rules would be:
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:
In the above case, typescript can only type this as any.
Thoughts?
The text was updated successfully, but these errors were encountered: