-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Abstract Properties #4669
Comments
+1 Also abstract getters should be full-fillable by properties: abstract class A {
abstract get p();
}
class B extends A {
p;
} This is a pretty common pattern in Dart when the abstract class just need some values by it's implementers. |
+1 angular has been wishing for this as well: |
declaring the property as abstract does not change anything different from just declaring it. the type is going to always have the property. As for getters and setters., the type system does not differentiate between getter/setter and a property declaration. |
@mhegazy Actually, there is an important difference. Using an abstract property I could specify that the property must be specialized (usually with getters and/or setters) in a derived class, and the compiler should rise an error if it's not specialized. Please reconsider closing this proposal. |
The lack of abstract properties allows child classes to simply ignore some important properties in my contracts. Please reopen this for further discussion... |
+1 Especially as lambda/arrow functions are actually properties as I found out when I was trying to implement the following functionality but it wouldn't compile due to.... "Class 'Base' defines instance member function 'def', but extended class 'Concrete' defines it as instance member property"...
|
Please reopen for discussion! Here is a simple example describing the problem: abstract class Foo {
abstract getName(); //works like a charm
abstract setName(value);
/* but it's better to use properties:
abstract get name();
abstract set name(value);
*/
/* I can define properties */
private _name = '';
get name() {
return this._name;
}
set name(value) {
this.name = value;
}
/* but cannot force Bar class to implement them */
}
class Bar extends Foo {
//complains only about getName and setName
} Compiler complains about undefined getName and setName but I can't define property contract. |
+1 |
+1 |
@raveclassic I'm interested in your request but I don't yet understand why the class inheriting from the abstract class needs it's implementation of an attribute to be enforced using getters and setters. Should it not be the responsibility of the child class how it implements an attribute? In your example, what is missing with:
If someone implements another class without specifying the attribute at all, or by redeclaring it as a normal attribute:
...in all three classes (that inherit from the abstract class) they have a valid and functioning |
(I unfortunately haven't yet grasped with @craigbroadman is referring to but) perhaps @vagaroso, @tehsenaus, @sirudog or @denvned could also help illuminate what's missing from the examples I have shown in my previous comment please? |
@AJamesPhillips The main reason in using abstract getters is that if I leave export enum DataEventType {
TRADE,
QUOTE
}
export type DataTO = TradeTO | QuoteTO;
export abstract class DataEvent {
protected _instrument:Instrument;
protected _data:DataTO;
get instrument():Instrument {
return this._instrument;
}
get type():DataEventType {
return this.getType();
}
/**
* todo: make abstract when typescript supports abstract getters
*/
get data():DataTO {
return this._data;
}
constructor(data:DataTO) {
this._data = data;
this._instrument = this.createInstrument(data);
}
protected abstract getType():DataEventType;
protected abstract createInstrument(data:DataTO):Instrument;
}
export class TradeDataEvent extends DataEvent {
get data():TradeTO {
return this._data as TradeTO;
}
protected getType() {
return DataEventType.TRADE;
}
protected createInstrument(data:TradeTO):Instrument {
return Instrument.fromString(data.symbol);
}
}
export class QuoteDataEvent extends DataEvent {
get data():QuoteTO {
return this._data as QuoteTO;
}
protected getType() {
return DataEventType.QUOTE;
}
protected createInstrument(data:QuoteTO):Instrument {
return Instrument.fromString(data.symbol);
}
} In the example above I need all |
Hey @raveclassic thanks for the clarification. I tried to reduce the example to highlight the essence of what you wanted to achieve so let me know if I've missed a crucial requirement / component. As you can see you're opening yourself up to errors using casting:
I guessed at what you're trying to achieve, again correct me if I'm wrong, but I think a better pattern for this would be the following. Whilst I was trying to produce this I did think at one point that abstract setter or getter would again be useful, in this example because the setter does something each time with the
Hope that helps. |
@AJamesPhillips Interesting, somehow I forgot about generic type guards in class declarations.. =) UPD: As for @craigbroadman example, I can imagine a very common situation when using React and instance-bound handlers: abstract class Popup extends React.Component {
abstract onClose(): void; //cannot use 'abstract onClose: () => void`
render() {
return (
<button onClick={this.onClose}/>
);
}
}
class CustomPopup extends Popup {
_onClose() { //correct but unbound
}
onClose = () => { //this is an instance-bound handler
//here the compiler complains aboud difference in onClose declarations
//trying to access 'this'
}
} One solution is to always bind handler either when passing to inner components: |
@AJamesPhillips
The issue is at the moment there is no way to express that a property must be implemented in some way by a subclass. I'm forced to do things like this:
Yuck! |
Yes @tehsenaus and I don't have any answer to that. Do you have a concrete example we could look at? One where you are prefably exporting an abstract base class that you want the software engineer using that class to know at compile time that they need to give an
Why should the abstract class enforce on its descendants the necessity to define a property when it could just do the following (sorry tone of this sounds a bit aggressive... Not meant to be I assure you :) ) |
I can't give a concrete example without sharing proprietary source... but let's say I'm implementing the 'template method pattern', where the abstract class needs to read a property from a subclass. I could use a getter method... but why? This isn't Java! |
No obviously that's fine @tehsenaus, good example though... and what is undesirable about specifying the property in the abstract base (sorry if I'm being stupid and have just missed the point of 'template method pattern'). |
@AJamesPhillips I think that Also, what do you think about instance-bound handlers which are described in my previous message update? It seems like I've updated it too late and you haven't noticed that. =) |
Yes @raveclassic I did respond as soon as I was notified :) Re "instance-bound handlers" I don't understand why the Re: "template method",
That's correct, but we're referring to properties. As mhegazy said earlier:
|
@tehsenaus Sorry I don't understand. I agree that:
I also think that:
Again even a partially concrete use case would be very useful to understand what problem you are trying to solve and how you think abstract properties would help. Is it that you want the abstract class to force the subclass to initialise the property? Or provide some particular functionality? Or something else? |
Dart have abstract properties and uses them quite a lot in the standard library in a very nice way. I can't see how a language that have getters and setters can justify abstract methods but not abstract properties. They are the same thing just with a nicer syntax. If you don't think abstract properties are needed you should think that abstract methods are equally needless. |
"abstract properties are the same thing just with a nicer syntax." @Pajn I agree. And of course abstract methods are valuable, they force subclasses to implement them. In a comment just before yours I have used one :) Thanks for the link to the dart library. So is it only a case for brevity? i.e. it would allow you to condense this:
To this:
Or is there a pattern in TypeScript that you just can't achieve without abstract properties? I've tried creating one but failed so far. I can see how it saves you several lines of code but personally I don't mind the Java like getters and setters, I'm primarily interested in patterns you just can't currently implement with TypeScript. |
Just as abstract properties forces subclasses to implement them. They are the same thing.
Mostly, however I would argue that it's about intention as well. A property should never have a side effect, methods might. Mixing Java style getters with good getters makes the code more confusing.
We could probably think of some strange edge case where some library expect a good getter but I would say no. You can manage just fine without them. But you can manage just fine without abstract methods as well by having an abstract class implement an interface. But that doesn't mean that it's the clearest way to express yourself. I would totally understand declining abstract properties if there were no abstract methods but when you already have the concept of abstract things I can't understand why not all methods can be abstract. It makes the language harder to learn and understand when you have to remember all special cases for different kinds of methods. |
Is this going to be reopened? I think we've made a strong case for abstract properties. |
Use cases keep piling up indeed. At a hesistant 'yes' but we'll take this to the next design meeting for wider discussion. |
If we get this, the use-case for #4670 could be solved in a clean way as we would be able to redirect the responsibility to fulfill the interface by declaring all parts of the contract (both properties and methods) as abstract and to be implemented by the derived class: interface A
{
p;
m();
}
abstract class B implements A
{
abstract p;
abstract m();
}
class C extends B
{
get p() {return null};
set p(v) {};
m() {}
} Some extra typing compared to the original request, but that could at the same time be seen as increased clarity of intention. Otherwise I don't know how to cleanly achieve derived classes that have a common abstract class with common behavior and at the same time enforce that the derived classes have certain properties without having unity types everywhere (which feels more like a hack for this scenario anyway). As I see it, the general concept of the "AbstractClass" is basically a subclass of an "Interface" with the addition of the possibility of having some members implemented. So it would make total sense if "abstract class" indeed is a superset of an interface, which it can not be unless the abstract class can do all the things an interface can do. Just like you can do in C# interface A
{
string P { get; set; }
void M();
}
abstract class B : A
{
public abstract string P { get; set; }
public abstract void M();
}
class C : B
{
public override string P { get; set; }
public override void M()
{
throw new NotImplementedException();
}
} |
@mhegazy hand this one off? |
PRs are welcomed also. |
Note, the conclusion form the design discussion was |
#7184 allows for abstract properties (and accessors). There's not much to the change because the machinery is the same as for abstract methods. |
Awesome 👍 Which version is this landing in? |
It should already be in |
This is a proposal to extend #3578 to allow abstract properties in abstract classes. So the following should be legal:
The text was updated successfully, but these errors were encountered: