1

I am trying to make changes to the alpha method within Cat and have beta reflect those changes.

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

bar: 3 has an error

Argument of type '{ foo: string; bar: number; }' is not assignable to parameter of type '{ foo: string; }'. Object literal may only specify known properties, and 'bar' does not exist in type '{ foo: string; }'.(2345)

Playground

Is there some way to update this beta without copying it over? Perhaps a decorator?

Here's one possible idea:

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

Idea 2:

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

1 Answer 1

2

The type of subclass-relative typing you're looking for should be possible with a polymorphic this type:

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

Here we've explicitly specified the type parameter in wapperFunction as this["alpha"] instead of allowing the compiler to infer it as (x: {foo: string}) => string. The difference is that this["alpha"] means "whatever the type of the alpha method is on the subclass you're looking at".

Then the following should work for you as you expect:

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

Note that polymorphic this types are easier to use but harder to implement. If you try to do something with this.beta() inside of your FourLegged or Cat implementations, you will likely get a compiler error. So beware.

Okay, hope that helps; good luck!

Link to code

Sign up to request clarification or add additional context in comments.

1 Comment

This does take care of making sure the typing works, but as pointed out here. Because beta is assigned in the constructor it never changes the function when alpha is added in Cat. So beta is still using the original this.alpha.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.