Changing a method within a class does not automatically update how it is used in other classes that inherit from it

I am attempting to modify the alpha method in the context of the Cat class, and have the beta method reflect those modifications.

const wrapperFn = <T extends (...args: any) => any> (a: T) => { 
    return (...args: Parameters<T>) => {
        return ''
    }
}

class FourLegged { 
    alpha = ({ foo }: { foo: string }) => foo
    beta = wrapperFn(this.alpha)
}

class Cat extends FourLegged {
    alpha = ({ foo, bar }: { foo: string, bar?: number}) => (foo + bar)
}

const c = new Cat()

c.beta({ foo: 'foo', bar: 3 })

The error lies in the bar: 3 parameter

The argument '{ foo: string; bar: number; }' is not compatible with the parameter '{ foo: string; }'. Object literal can only define known properties, and 'bar' is not present in type '{ foo: string; }'.(2345)

Playground

Is there a way to update the beta method without duplicating it? Maybe using a decorator?

One potential solution could be:

const wrapperFn = <T extends (...args: any) => any> (a: T) => { 
    return (...args: Parameters<T>) => {
        return ''
    }
}

const example = (value: typeof replacements) => class FourLegged { 
    replace = value()
    alpha = ({ foo }: { foo: string }) => foo
    beta = wrapperFn(this.replace.alpha || this.alpha)
}

const replacements = () => { 
    const alpha = ({ foo, bar }: { foo: string, bar?: number }) => (foo + bar)
    return { alpha }
    // return { alpha: null }
}
class Cat extends example(replacements) { }

const c = new Cat()

c.beta({ foo: 'foo', bar: 1 })

Another idea:

abstract class FourLegged <replaceAlpha extends (...args: any) => any> { 
    replaceAlpha: replaceAlpha | null = null
    alpha = ({ foo }: { foo: string }) => foo
    beta = wrapperFn(this.replaceAlpha || this.alpha)
}

class Cat extends FourLegged<Cat['replaceAlpha']> {
    replaceAlpha = ({ foo, bar }: { foo: string, bar?: number }) => (foo + bar)
}

Answer №1

To achieve the subclass-relative typing you're interested in, consider utilizing the concept of a polymorphic this type:

class FourLegged {
    alpha = ({ foo }: { foo: string }) => foo
    beta = wrapperFn<this["alpha"]>(this.alpha)
}

In this example, by specifying the type parameter in wrapperFunction as this["alpha"], you are effectively referring to the type of the alpha method within the subclass being utilized.

Subsequently, the following implementation should work according to your preferences:

class Cat extends FourLegged {
    alpha = ({ foo, bar }: { foo: string, bar?: number }) => (foo + bar)
}
const c = new Cat()
c.beta({ foo: 'foo', bar: 3 }) // no error

It is important to note that while using polymorphic this types is convenient, they can be challenging to implement. Attempting to access this.beta() within the FourLegged or Cat constructions may result in a compiler error.

Hopefully, this explanation proves to be beneficial for your needs. Best of luck with your implementation!

Link to code

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Is there a way for me to retrieve the callback parameters?

Can the parameters of the callback function be accessed within the 'outer' function? function f(callback: (par1: string)=>void): void { // Is it possible to access 'par1' here? } ...

Encountering an issue with the message: "Property 'ref' is not available on the type 'IntrinsicAttributes'."

Having trouble implementing a link in React and TypeScript that scrolls to the correct component after clicking? I'm using the useRef Hook, but encountering an error: Type '{ ref: MutableRefObject<HTMLDivElement | null>; }' is not assi ...

While utilizing Typescript, it is unable to identify changes made to a property by a

Here is a snippet of code I am working with: class A { constructor(private num: number = 1) { } private changeNum() { this.num = Math.random(); } private fn() { if(this.num == 1) { this.changeNum(); if(this.num == 0.5) { ...

How can I compel npm to resolve dependencies flatly?

I am working on a project where multiple frontends share a common library. The module dependencies for these projects are managed using npm. In the package.json file of each project, I specify: "dependencies": { "mylib": "file:../<...path...> ...

An easy method to define argument types for every function type in TypeScript

How can I assign argument types to each function type in TypeScript? Each function has a parameter associated with it. [Example] type F1 = (arg: number) => void; type F2 = (arg: string) => void; const caller = (f: F1 | F2) => (n: number | strin ...

Navigating in Angular with parameters without modifying the URL address

In a nutshell, my goal is to navigate to a page with parameters without showing them in the URL. I have two components: Component A and B. What I want to do is route to B while still needing some parameters from A. I know I can achieve this by setting a ...

The function 'toLowerCase' cannot be found for the type 'string | number | string[]'. Similarly, the function 'toLowerCase' cannot be found for the type 'number'

Currently, I am working on a Laravel project using Laravel Mix. I am attempting to create a table with filter functionality. However, when I insert the following code into my TS file: import $ from 'jquery'; import 'bootstrap'; $(() = ...

Using Typescript: Defining a function parameter that can be either of two interfaces

While browsing through this specific question, I noticed that it was somewhat related to my current issue, although there were notable differences. In my scenario, I have a function named parseScanResults which accepts an object as its argument. This obje ...

Add a new child component template with each click using the onclick event in Angular

Is there a way to dynamically add a child component with each button click event? Here is the HTML code for the button: <button type="button" class="btn btn-success btn-sm btn-add-phone" (click)="addfield()"><span class="fa fa-plus"></span ...

Ways to extend the default timeout duration in Angular

My server calls are taking a long time, around 30-40 minutes, and my Angular frontend is timing out. Is there a way to increase the default timeout for this service call? method1(id: number): Promise<number> { const body= JSON.stringify(id); ...

Return to the previous page with different query parameters, not the same one

When it comes to reverting state location back by 1 step in Angular, we can utilize something along the lines of this.location.back();. This method works well unless the system redirects to the same URL but with different query parameters. In such cases, ...

Guide on sorting an array within a specific range and extracting a sample on each side of the outcome

I need a simple solution for the following scenario: let rangeOfInterest = [25 , 44]; let input = [10, 20, 30, 40, 50, 60]; I want to extract values that fall between 25 and 44 (inclusive) from the given input. The range may be within or outside the inpu ...

What is the best way to incorporate Typescript React Components across various projects?

I'm venturing into developing an npm package that involves several react components implemented with typescript. As a newcomer to react and npm, I apologize if my query seems basic. Despite researching online, there isn't much information on this ...

Getting the Correct Nested Type in TypeScript Conditional Types for Iterables

In my quest to create a type called GoodNestedIterableType, I aim to transform something from Iterable<Iterable<A>> to just A. To illustrate, let's consider the following code snippet: const arr = [ [1, 2, 3], [4, 5, 6], ] type GoodN ...

Having trouble with the Angular Material component? The element 'mat-option' is not recognized

I am having trouble with implementing an Angular Material component. The component is not functioning properly, and I received the following error message: Uncaught Error: Template parse errors: 'mat-option' is not a known element: // ... I sus ...

Steps for incorporating a new element in Reactjs

While attempting to insert a new element into a React object, I encountered the following issue: const isAdmin = true let schema = { fname: Yup.string().required('Required'), lname: Yup.string().required('Required&apo ...

Extending a type by adding properties from separate files using TypeScript

I am faced with a situation where I have a file containing either a type or interface variable (all of which are exported), and I need to add new properties to this variable from different files without using extends. Essentially, making changes to the sam ...

Exploring the differences between Typescript decorators and class inheritance

I find myself puzzled by the concept of typescript decorators and their purpose. It is said that they 'decorate' a class by attaching metadata to it. However, I am struggling to understand how this metadata is linked to an instance of the class. ...

A loop in JavaScript/TypeScript that runs precisely once every minute

Here is a snippet of my code: async run(minutesToRun: number): Promise<void> { await authenticate(); await this.stock.fillArray(); await subscribeToInstrument(this, this.orderBookId); await subscribeToOrderbook(this, this.orderBookId ...

Mastering regular expressions in TypeScript

My goal is to perform linting on staged files that are either .ts or .tsx and located within the src folder. I am aware that for selecting all js files one can use "*.js": [--list of commands--] inside the lint staged property. I'm curious to learn m ...