Skip to content

Support for optional abstract members in an abstract class body #22939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Jessidhia opened this issue Mar 28, 2018 · 5 comments
Closed

Support for optional abstract members in an abstract class body #22939

Jessidhia opened this issue Mar 28, 2018 · 5 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@Jessidhia
Copy link

Jessidhia commented Mar 28, 2018

TypeScript Version: 2.8.0-insiders.20180320 (version currently running in playground)

Search Terms:
abstract optional

Code

abstract class Foo {
    abstract foo(): void
    abstract bar?(): void
}

// Non-abstract class Bar missing abstract member bar
class Bar extends Foo {
    foo() { return }
}

Expected behavior:
It should be possible to omit the definition of bar in the Bar class.

If something constructs an instance of Bar, it should not be possible to call new Bar().bar(), either because it is considered undefined or because it still retains some "abstractedness".

The intent is to use this in type definitions where subclasses can optionally define methods that will be called by their superclass; similar to lifecycle events in React.Component.

Not making them abstract allows things such as calling super.componentDidMount() even though it does not exist in React.Component's prototype, or having a subclass without their own render() implementation because TypeScript thinks it is provided by React.Component.

Supporting optional abstract members could also be used to guard against the common error of not initializing state on classes that use this.state -- it does not actually exist until either the first setState call or until this.state is assigned, and initializing this.state by assignment is far more common than initializing it with setState.

Actual behavior:
It's not possible to omit it unless bar = undefined is specifically added to Bar

Playground Link: Playground Link

@mhegazy
Copy link
Contributor

mhegazy commented Mar 28, 2018

and why not just:

abstract class Foo {
    abstract foo(): void
    bar?(): void;
}

@Jessidhia
Copy link
Author

Because this means that super.bar might exist, but it cannot.

@mhegazy
Copy link
Contributor

mhegazy commented Mar 28, 2018

but how would you witness that? you can not just call super.bar() since it is potentially undefined, so you have to check for its existence... and if so, what difference does it make if it is abstract or missing?

@mhegazy mhegazy added the Working as Intended The behavior described is the intended behavior; this is not a bug label Jul 18, 2018
@typescript-bot
Copy link
Collaborator

Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

@simeyla
Copy link

simeyla commented Oct 2, 2019

You may not need abstract at all

I've hit this issue a few times, and one possible approach is to do this - without the abstract keyword.

class Animal
{
    // not all animals can bark so the default is to return undefined
    bark(): AnimalBarkStatus { return undefined; }     // no abstract keyword needed
}

class Dog extends Animal
{
   bark() 
   {
      return 'not-the-correct-type';   // compile time error
   }
}

One of the reasons you may think you want 'abstract' is to enforce the method signature. So that 'bark' will return the expected type in any subclass.

If you convert the above code to C# it will compile, because it's allowed for the subclass to return a different type. And of course C# has virtual and override that typescript currently does not.

However in typescript if the subclass doesn't return the same type then it just won't compile.

Property 'bark' in type 'Dog' is not assignable to the same property in base type 'Animal

I had cases where I had an optional method in 10% of subclasses, but I wanted the signature to be enforced by the base class if it did exist. The above allows me to do that without any extra keywords.

The only thing that may not be desirable is that the base class method returns undefined - but for me that was just fine.

Also even without 'abstract' you can rename the baseclass method and (at least in VSCode) it will rename all the subclass methods too :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

4 participants