Search code examples
reactjstypescriptdesign-patternsinterfaceecmascript-next

is there any way to bypass ts 2339 in to access class methods OR is it possible to define class methods outside of class in typescript?


What I intend to do is as follows

class A {
  constructor() {
    bind(this); 
  }
  hello(){
    this.method1();  // <-- I will get error at this line saying method one does not exist on typeOf A
  }
}
function bind(thisReference) {
  function method1() {
    console.log('in method 1');
  }
  thisReference.method1 = method1.bind(thisReference)
}
var a = new A();
a.hello();

Please note there are other cases of object where we get ts2339, which can be solved either by defining types or by using any

I am particularly looking to solve this case and therefore separate question. It is very useful when defining class components in react, so that we can easily define new methods on class outside of class.


Solution

  • [SOLUTION] Thanks to @Bergi, for inspiration

    This error seemed so limiting, that I thought that I would better keep my js file, then to create ts file and add the extra typings

    However I realized this error is not because of typescript's rules, it's because of how classes are different from functions, although classes in javascript transpile to functions, yet they have their unique existence

    We certainly can't bind member variables to classes outside classes, but my main intent was that a class should be able to delegate logic to some code outside the class and that can be accomplished with following syntax which is sweet and simple

    import React from 'react';
    
    /** this should not be arrow function so that
     *  caller's execution context can be used as this */
    function sayHello() {
      console.log('Hi, these are the props', this.props);
      this.setState({ a: 'hi' });
    }
    
    export class Delegate extends React.Component<any, any> {
      state: any = {};
        sayHello = sayHello; // <-- that which adds sweetness!
        /**
         * note the use of arrow function to define hello
         * so that when it calls this.sayHello
         * this actually refers to class instance
         */
      hello = () => {    
        this.sayHello(); // <-- this works totally fine
      };
      render() {
        return (
          <div>
            Delegate Class
            <div onClick={this.hello}>
              this is coming from props {this.props.propName}
              {this.state.a}
            </div>
          </div>
        );
      }
    }
    ---
    <Delegate propName="propName" /> //will do as expected
    ---
    var a = new Delegate();
    a.hello(); // prints --> 'will work properly'
    

    Note

    1. we didn't use the constructor function over here
    2. instead of binding outside the class, we are binding directly in class declaration

    EXTRA POINTS -- USE CASE

    Use case can be seen two-fold

    1. suppose there are two classes A and B that want to have a method, then one way is to go with extends, write a Class Super having that function, another way is to use the assign method to class variable as we discussed in the solution, I think, this approach is more liberal way of writing, that means A and B can exist independent of the parent class.

    on the otherhand writing code with extends is also not scalable, if there is another function which class A and C wants to use then we have to scratch our head

    1. if there is no use case as described in point 1, then this syntax helps reduce the number of lines of code of class A, since it has delegated function definition to another file.

    I prefer writing files with as much less code as possible per file