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

Fixing undefined 'this' in Javascript #24636

Closed
4 tasks done
olmobrutall opened this issue Jun 3, 2018 · 4 comments
Closed
4 tasks done

Fixing undefined 'this' in Javascript #24636

olmobrutall opened this issue Jun 3, 2018 · 4 comments
Labels
Duplicate An existing issue was already created

Comments

@olmobrutall
Copy link

olmobrutall commented Jun 3, 2018

Search Terms

undefined this

Suggestion

With structural typing, generics, union, conditional types, etc. Typescript has reached the heaven of sofistication, but one (the?) elephant in the room is the annoying behaviour of 'this' in typescript.

https://twitter.com/bendhalpern/status/578925947245633536

I know that TS has a way to define the type of the this parameter, but does nothing in checking whether this is going to be bound or not by the time the function runs.

The problem

class MyComponent : React.Commponent {

     constructor(){
          this.handleCickBoundMethod = this.handleCickBoundMethod.bind(this);
     }

     handleClickLambda: ()=>{  
         this.forceUpdate(); 
     }
     handleClickMethod(){
         this.forceUpdate();
     }
     handleClickBoundMethod(){
         this.forceUpdate();
     }

      render(){
           return (
            <div>
               <button onClick={this.handleLambda} /> //OK
               <button onClick={this.handleMethod} /> //Error!
               <button onClick={()=>this.handleMethod()} /> //OK
               <button onClick={this.handleClickBountMethod} /> //OK
            </div>
           );
      }
}

The solution

Every class method (not lambda) that uses this (directly or in a inner lambda) is internally marked in the compiler as 'unbound-this'.

Methods marked with unbout this can be only called as an instance method, and can not be assigned to variables or passed as parameters.

This mark can be reverted with some cast or using bind.

<button onClick={this.handleMethod} />//TS Error: this will be undefined. 
<button onClick={this.handleMethod as (()=>void)}> //Avoid TS error at your own risk. 

I think lambda capturing is considered:

<button onClick={()=>this.handleMethod()} />

In this case, the lamda is already capturing this, and we are just calling the method not passing it, so should be ok.

Limitations

When this is used in normal functions outside of classes, (where you need the explicit this parameter in strict) this tracking will not help (for now).

function joinComma(this any[]){
    return this.join(", ");
}

Array.prototype.joinComma = joinComma; 

[1, 2, 3].joinComma(); //Ok
joinComma([1, 2, 3]); //Runtime error, still not solved in TS. 

Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript / JavaScript code
    It will add aditional errors, but that's the 'name of the game'.
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. new expression-level syntax)
@mhegazy
Copy link
Contributor

mhegazy commented Jun 4, 2018

--noImplicitThis handles a set of these scenarios. there are set that were not handled because of implementation issues. the remaining scenarios are tracked by #10288

@mhegazy mhegazy added the Duplicate An existing issue was already created label Jun 4, 2018
@olmobrutall
Copy link
Author

I'm working with strict for a long time, and I'm sure TS is not caching this issue. Is not about the type of this. Is about making sure this will be bound by the time the method is called.

This should be forbidden:

class Person {
    name: string;
    greeting() { console.log(this.name); }
}

var p = new Person();
p.name = "John";
var gr = p.greeting;  // Error, you can not get a naked fointer to a funtion that requires this, without a casting

@mhegazy
Copy link
Contributor

mhegazy commented Jun 4, 2018

that is what #10288 tracks.

@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants