Is it possible to define method outside the 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.

Hello. Please add content to your post to invite discussion rather than only posting a link to a question you asked elsewhere. Thank you.

So 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

so I 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

function method1() {
  console.log('in method 1');
}

class A {
  method1=method1 // <-- that which adds sweetness!
  hello = () =>{     //  <-- Note the use of arrow syntax, so as to pass this, that refers to class
    this.method1();  // <-- this works totally fine
  }
}

var a = new A();
a.hello(); // prints --> 'in method 1'

Note

  1. we didn’t use the constructor function over here
  2. Just calling with proper this is sufficient

I’m gonna say a similar thing to last time: React is not really OO (specifically classical, Java-101 style OO), and that you are attempting to write in a programming style that imo contains serious OO antipatterns anyway, ones that will make your code brittle and difficult to debug and refactor. In the previous case, you deliberately wanted to increase the coupling between components by introducing direct inheritance. This time you want to directly couple functions to specific objects at a class definition level.

This is somewhat pointless because:

  • if it’s supposed to be reusable, then why is it not just a function you call in the class method? Why introduce coupling?
  • if it’s supposed to be part of the class, then why is it seperate? Why the extra few function calls for no reason at all?
  • if it’s supposed to be a decorator, why not use decorators (or just decorator pattern if you don’t want to use the experimental syntax)?

And w/r/t React-specific patterns, classes are just used by the framework as a convenience. The framework actually works much closer to how the function hooks API has been exposed. You have a function which takes some inputs, returns the result of that (createElement(your input)). And you have tree structures of these, with one node at the top. And if you run a function it affects its children.

It is like an S-expression. It is not an isolated, class based OO system that works via message passing across object boundaries, and callbacks are used liberally to cause functions higher up the tree to reexecute.

Then you pass that to a function that converts if to HTML (eg ReactDOM.render) and you get HTML out the other end.

Edit: IRL, if you wrote React code the way you’re trying to within a team environment, all your code would need to be rewritten to be more idiomatic: you’re attempting to write imperatively for a framework that expects a declarative coding style

1 Like

Thank you for the inputs!

Yes , I guess, this code can be changed with decorator.

But what good reason is there for doing this instead of just literally writing a function? I’m not saying don’t. It’s just that you need a really good reason for introducing a layer of complexity over the top of how code for the framework would normally be written. Decorators don’t make things simpler; they may make things easier, but the tradeoff is that you’re adding unidiomatic complexity: you’re modifying the normal API

The example you used doesn’t do anything. What is an actual usecase

Thank you Dan for asking,

It can be seen two fold

  1. suppose there is a function which two classes A and B wants to implement, then one way is to go with extends, write a Class Super having that function, other way is to use the bind as we discussed in the solution, I think, going with bind is more liberal way of writing, that means A and B can exist independent of parent class.
  • plus writing code with extends is not scalable, if there is another function which class A and C wants to use then it adds the difficulty
  1. if there is no use case as described in point 1, then this bind 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

But this clearly doesn’t produce less lines of code. JavaScript has free functions, they don’t need to be part of some classical object heirarchy. Literally just write a function, call the function in the lifecycle method/handler. What do you gain by explicitly binding it to a class, by adding patterns and complications on top of this?

Actually I am using this specifically in context of react.

I updated the code above, we do not need to bind even, just calling with proper this is sufficient.

Yes, It does not produce less line of code, [ --> It produces less lines of code PER FILE ] that’s what I try, that each files should be as small as manageable

Code golf, either across the project, across single files, or inside individual components, really is not a great goal. You want robust code in as simple and straightforward of a design as possible. Playing golf for the sake of playing golf makes bad code, especially when you introduce antipatterns to play golf.

But hey, it’s your codebase and your future debugging and refactoring, not mine.

Hi Jeremy, Thank you I learned a new thing Code golf.

However code golf is only being talked in second point, first point is pure helpful use case, what you think?

If the function will work for a variety of disparate classes, why go through the effort to bind specifically to those classes?

2 Likes

What I’m getting at is why are you binding the functions to a specific instance of a class: what does this gain you over just using a function? If you have a general purpose helper function, why not just use it? Why go through the rigmarole of binding it some object? It isn’t more code to just use a function, it’s less. All I can see that you’re doing is adding code to purposely tightly couple functionality.

Again, the example you’re posting doesn’t explain any [irl] benefits, do you have an actual concrete example?

Edit: this doesn’t automatically make it more manageable. The tradeoff is that that related functionality is no longer co-located (so instead of just looking at a single file, you need to spelunk through several). You’re increasing the edit distance (literally, if you’re editing, you’ve increased the distance from a few lines in the editor to a having to locate a related thing in another file or files). It’s constantly necessary but it’s a tradeoff, lots of tiny files aren’t just a thing you should automatically want

1 Like

I agree to both of the arguments given by you

  1. why go through effort of binding
    basically instead of defining sayHello = sayHello in class declaration, I could call sayHello.apply(this) and it works equally

I thank you for raising this argument, so we just come to know two ways of working, whichever is better can be chosen, class declaration seems standard, and calling with apply feels quite liberal

  1. I have removed the colocation and increased edit distance

Yes, I agree, that I have increased edit distance, i guess, this is somewhat biased of me, basically why I took this step is because, as soon as I open my React Class component, i do not want to see the multitude of helper functions of class, instead my react class component should only contain the rendering logic, rest all the helper function can be extracted to separate file.
yes but I did this at the cost of increasing edit distance.

So i am happy for both of your arguments. Thank you for throwing light!
Thank you again!

here is my Related React code

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 = () => {
    console.log(this);
    sayHello.call(this);
    // 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>
    );
  }
}

You shouldn’t need to use apply. You shouldn’t need to be borrowing methods here. Look:

// helpers.js
export function helper() {
  // do something
}
import { helper } from "./helpers"

export class MyComponent {
  someHandler() {
    helper();
  }

  render (
    <button onClick={this.someHandler}>Do thing</button>
  );
}

State is local to the component. You are writing code that mutates that state in different parts of your codebase. For very simple stuff, sure it’ll work, but it will be an absolute nightmare to work with once you get anything mildly complex. What you’re doing is tightly coupling disparate pieces of functionality, building inheritance chains and therefore producing extremely brittle code. I get why you’re doing it, but it’s not a good pattern in standard classical OO code, never mind React which makes much more use of functional idioms.

1 Like

Yes, it’s only because of React, that such a pattern is helping me,

My code is having 5 levels of composition, and it feels quite easier to call helper function to mutate 1st level parent state from 5th level component, just by .call(this) [this refers to class component at 1st level, remember my previous question of allowing to pass parent reference to child :slight_smile: ]

All the more this 5th level component, has to be used in other parents as well, and there as well it will do the same thing, as done by helper

I guess I still don’t see the benefit of intentionally making things more tightly coupled with 5! levels of composition. This sounds like a complicated tangle of a design that you are making more complicated and more tangled by coupling things more tightly. This sounds like a really fragile design.

1 Like

So the aim of this is to deal with prop drilling by writing your own API to bypass what React provides out-of-the-box? You are still going to have to do the same thing as the standard way, but now you’ve added your own complex imperative API on top for no good reason. The only way you’re going to be able to hold onto the state and pass it to a child component is if you bind in the component then pass that bound function on – ie create a [global] container to hold the state – but this is pointless because React already provides Context, which works absolutely fine, you don’t need to write a jerryrigged implementation to use instead of it.

I realise you’re just going to do this regardless, but for the nth time this is a terrible pattern to use in React. In a framework based on functional principles that depends upon composition, one-way data flow and extremely explicit passing of values, you are purposely creating an extremely imperative API with tight coupling and inheritence chains based on implicit object references. Dealing with state is generally the most difficult part of frontend app work, and you’re purposely making it more difficult by obfuscating things. It won’t work well. Use functional, declarative patterns, rather than trying to hold onto object instances via binding this values.

1 Like

@JeremyLT, @DanCouper,

Yes, actually I am trying to add a small new feature, over existing code, just trying to tweak it to look more simpler.
But I am not coupling things more tightly, I am actually decoupling things, so when I removed those class member functions, out to helper file, I decoupled their logic from the class and now few other classes can also use the same helper logic if need be

Dan you are correct in the when you mention Context, yes Context is way to go over here, however, I thought that would be a lot of code change, so I didn’t took that path.

I have noted your point about not binding functions to class, I am skipping those for certain helper functions, but we could do this only because I extracted them out of the class, if they were in class, they were already tightly coupled and unusable for other class

So I don’t see, how I am introducing tight coupling, I see I am doing opposite.

With a bit of respect, I don’t understand why it would be a terrible pattern, I would say something terrible, if it introduces extra work, without offering any benefit or solving a problem, i.e. it could have been easily solved through other simpler way.

If you leave away context, what is that which makes it terrible, and I had to leave context, because that would ask either to change my class component into functional component, which is good, but would have demanded lot of logic rewriting OR use the renderProps pattern, both was too much of rewriting

You are using this. And you are using it in “helper” functions. So that function needs to explicitly know what the callee object is. So either they are just methods that belong to a specific class, in which case there is zero reason for them not to be in the class definition, or you now need to have a specific defined contract between certain classes and certain functions that both need to know about.

Functions in a codebase with a free floating this in them is a massive code smell.

2 Likes