Is it possible to define method outside the class in typescript

I think you misunderstood the term helper function a bit. Helper function should not have reference to this as it indeed creates coupling. For sayHello to be uncoupled function it should look something like this:

const sayHello = (name) => ({ a: `${name} says hi!` });

// And later inside component:
this.setState(sayHello('snigo'));

This way logic is out of component and helper function is independent from the component at the same time

1 Like

Yes, the second option is right.

we need to have a specific contract defined between certain classes and helper function.
So the name of my state class is Compliance.tsx and name of helper file, which has these function is ComplianceStateHelper.ts Now there could be other class like ComplianceHistory.tsx and it might also use these helper functions from ComplianceStateHelper.ts

so contract has to definitely be defined. you actually are deep, I have to mention that I am kind of at fault for not giving all the details at once.


Functions in code base with free floating this is a massive code smell ā€“ I will read more on this, for it will take me some time to learn on this.

but that pattern is how we use the compositional inheritance pattern

function FCat(name) {
  this.name = `${name} + [FCat]`;
  Object.assign(this, meower(this));
}

function meower(state) {
  /** type 1 : setting property directly over the object
   return {
     ...state,
     meow() {
       console.log(`${this.name} is meowing`);
     },
   }; 
*/

  // type 2 : Better Approach, setting property on prototype of object
  const proto = Object.getPrototypeOf(state);
  proto.meow = function () {
    console.log(`${this.name} is meowing`);
  };
}

And this is the fragile part. You are making constraints on the class away from the class definition.

If a function is part of the class, it should be part of the class. If a function is not part of the class, then it should not obligate the class to have a specific internal representation.

1 Like

Thank you @snigo, I would have to agree with you!

What would we call such functions, which help us create compositional inheritance.

composer functions?

so do you think itā€™s ok to have classes with 500 lines of code what are the ways to make the files more readable

  1. one way is inheritance, but that is again a bad pattern, I am inspired by Eric Elliot, which says, that OO is itself very fragile.

your explanation about making class and functions fragile, is acceptable, but what is the solution over making the code more readable?

when I say readable, I mean we want to keep cognitive overhead less while reading a file. That means, just like we know a function should do only one thing, I was expecting, that a file, should just explain one thing or only few things, making sure that line of code do not exceed 150 may be, that number i will have to research what is best and another thing can be explained by another file

I prefer to have everything related to one class or implementation in one file. I donā€™t see a big deal with larger files.

But I think thatā€™s a separate question from how to implement composition without increasing cognitive overhead because you have hidden structural requirements for your class across a bunch of different functions.

yes, so basically, I have moved ahead from
one class per file
to
many files for same class
In the hope of reducing cognitive overhead, when reading one file related to that class

If it had to be done more properly may be creating a super class was the way to go forward. However I preferred compositional inheritance over here, over classical inheritance.

You can still do compositional inheritance without using this.. The two are unrelated.

Your implementation for a given method can be in a separate file and never use this.. The class calls the function with the arguments required, the function does its thing, and the class updates any internal data, as required, from the return value(s).

The function never sees or makes hidden requirements upon the internal representation of your objects and you loosen the coupling between components.

I actually did this as my first refactoring

I created function that accepts,

  1. state
  2. setState function
  3. relevant props from the class

But this became nightmare, as I had to wire every function invocation , accordingly (mostly for the third point)

but also I had to write extra function classConnector, but all this was not sweetCode

interface ConnectProps {
    state?: any;
    setState?: any;
    payload?: any;
}
/** it is important to bind the execution context of 
class as this to this file 
this is hof, which helps with wiring function invocation with custom payload
*/

export function classConnector(this: any, func: any, payload?: any) {
    return func({
        state: this.state,
        setState: this.setState,
        payload,
    });
}

export const onSubmitRFIRequest = ({
    state,
    setState,
    payload,
}: ConnectProps) => {
    const { raiseComplianceRFIRequest } = payload;
    setState({
        spinner: true,
        action: StatusEnums.complianceStatus.RFI_RESPONSE_RAISED,
    });

    raiseComplianceRFIRequest(state.rfiRequestsTobeSent)
        .then((res: any) => {
            setState({ spinner: false });
            if (res.type === 'ERROR') {
                setState({
                    showNotificationPopUp: true,
                    notificationType: 'error',
                });
            } else {
                setState({
                    showNotificationPopUp: true,
                    notificationType: 'success',
                });
            }
        })
        .catch(() => {
            setState({ spinner: false });
            setState({
                showNotificationPopUp: true,
                notificationType: 'error',
            });
        });
};
// and the extra prop drilling like this
<Component 
onChangeRfiComment={onChangeRfiComment}
onChangeDocumentType={onChangeDocumentType}
onConfirmPopup={classConnector.call(
    ComplianceInProcess,
    onConfirmPopup
)}
resetRfiRequest={classConnector.call(
    ComplianceInProcess,
    resetRfiRequest
)}
openAccordion={classConnector.call(
    ComplianceInProcess,
    openAccordion
)}
onRaiseRFI={classConnector.call(
    ComplianceInProcess,
    onRaiseRFI
)}
handleDownloadFile={classConnector.call(
    ComplianceInProcess,
    handleDownloadFile
)} />

But why use the class connector? That is explicitly increasing the coupling between components. Why not directly call the external functions in the class methods, passing in the data required by the function signature?

In composition, your objects implement interfaces. The interfaces should clearly describe what they require and what they do. The interface should not mandate internal data representation in your objects. That violates encapsulation.

@JeremyLT, Thank you for giving your time!

I am doing that only, I am calling an external function in class method,

classConnector is helping me curry it, itā€™s returning me a new function, that gets state and setState by default and I will pass the data needed.

you have to see that actually we are delegating class logic outside. That is the where question revolves,
yes actually that is where we are discussing a pattern, because we have this.props and this .state
,
these are the only two thing we are managing,

and actually encapsulation may not come into picture, since we are not going out of a class, we just have a class and set of (composer+helper) function in contract with that React Class

But I have to agree that my OO knowledge is not good, because I started directly with JS, I will work on it

I donā€™t know how to phrase this so that it becomes clearer, but:

You are using a library that effectively allows you to write a UI as a series of functions. These functions return the result of a function call or tree of function calls (createElement calls, which is what JSX desugars to). These are then nested within other functions. So what you get is the representation of a DOM structure built from the reult of a nested set of function calls. A React codebase is effectively producing something like an S-expression

Now, under the hood, this is an object structure, but React handles the references to these objects internally. What you are doing is trying to layer an imperative OO abstraction over the top of the public API. It is unequivocally more complex, and is completely unecessary.

What should give you pause for thought that maybe you are not doing the right thing by discarding the idiomatic approach to writing React code is that at no point in the code you write do you use the classes to instantiate objects. This should be a hint to you that the class-based API is a convenience, that it isnā€™t being used like in an OO codebase. As I said in an earlier post, the function based API (that is decoming the de facto standard way of writing React code) is much closer, visually, to how the library works. Itā€™s just functions, all the way down, very loosly coupled. Take some input (props) return some output. Keep it immutable as much possible. Donā€™t tie things together.


Edit: you mentioned Eric Elliot. Be a bit wary there. Iā€™ve learned a few things from his writings, if you dig in he has a few useful things to teach. But heā€™s also incredibly, aggressively self promotional and pushes for things that donā€™t quite seem correct a lot of the time. For example, pushing composition over inheritance (good), then presenting the One True Solution to thisā€¦which looks suspiciously likeā€¦inheritance. Take everything he says with a grain of salt, donā€™t blindly regurgitate his opinions. Many of them are contrary to common practice. This does not mean those opinions are wrong per se, but it is an indication they might not be as correct as he likes to state they are. Note that he tends to make up names for patterns, then write as if that name is the well known standard.

1 Like

My argument wasnā€™t really about the naming but rather about your whole approach. You really should start from following:

Because nothing about this is correct and Iā€™m starting to believe that this creates false belief framework for you.

You correctly said somewhere in this thread that React component ideally should do only one thing and this immediately creates at least two problems for you:

  1. Classes are containers of functions mostly and thatā€™s why using functions instead of classes in React is heavily suggested
  2. And you want to go further by adding couple more functions from outside on top of it

React components are functions and youā€™re composing WITH these functions and NOT composing those functions.

I will get back by day! lots to learn over here :slight_smile: