Description
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)