-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Properties with only getter or setter in an interface #814
Comments
See #12 |
#12 is a bad example - it deals with just a part of the problem, as, while 'getter only' is a more common case, setter only is also possible. And Immutability is not concerned here - since we are talking getters, another function in the object may well modify the value returned by our getter (the whole concept of Immutability is more about state, and accessors might not have state at all). We don't need a new keyword for the good old 'get' either. What is needed is a way to tell the compiler 'only get for property X is valid' or 'only set to property X is valid', analogous to what we can already do in classes implementing those interfaces. |
The We're definitely not bothering with write-only properties. This is not common enough to justify complicating the type system with it. If you could write some examples of how you think |
I'm not talking about reference type values, I'm saying that the whole concept of Immutability doesn't have much to do with a function, which a getter is. And if you look at it from a more duck typing point of view (how it can be used, not what it should be), consider the fullName example. I expect it to be get only, but not immutable at all, just mutated with other setters. That is why I don't think it is a good idea to mix discussion about getters / setters on an interface and immutability, or at least it should not be limited to that. As for write only properties - I don't think there is even a choice here, other then to make them possible in interfaces, unless you are willing to make them impossible in actual classes. And that, I think, would be a bad idea, considering they are present in ECMA 6. As long as JS supports them, so should you, imho. The final point - how is 'readonly' different from 'get', well, my train of thought is as follows. Should I see 'readonly' in TS code without prior knowledge, I would think like this - we already have 'get', so 'readonly' must mean something else (behaves slightly different in some way), because there is no point in having two names for the same concept. And that would lead to confusion. Also, I expect to see same syntax for such property on an interface and on the class implementing it. P.S. get is shorter :-) |
The syntax is largely irrelevant. Whether or not the keyword is |
The syntax is relevant at least because where there is a 'get' keyword - there should be a 'set' keyword, and #12 doesn't mention it in any form. Also, I wouldn't call calling things 'unimportant'. As for the example in #12 , since then there have been 3 or 4 requests for this feature and the authors either didn't find the original issue or initially considered it relevant to theirs (myself included). That is a sign that the original discussion is not what they are looking for. Btw, the case with point pt4 can illustrate why 'readonly' is bad. I COULD easily expect it to be immutable, not just by me, but anyone, and be puzzled why it changes between callbacks (by another function that had pt3 passed to it). I would never make that assumption about a 'get'. As I said earlier, I expect 'reasonly' to be associated with state, not a function, and constant state at that. As in 'nothing can change this by the time your code receives it'. Get is much less assumptive, basically, it conveys nothing beyond "you can get a value of this property"' |
There are no technical hurdles required to choose the syntax for this feature. The specific syntax used is not a factor in why this feature isn't yet implemented. While you may infer different things about the intended meaning depending on which word is chosen for the syntax the final semantics do not care about the chosen word, they simply must work.
Why? // #1 A read only property
interface Employee {
get name: string; // no setter exists, trying to assign a value to name will fail
}
// #2 A readable and writeable property with explicit get and set
interface Employee {
get name: string;
set name: string;
}
// #3 A readable and writeable property today
interface Employee {
name: string;
} The second form is redundant with the syntax that already exists. So the interesting case is the first, where there is only a getter and no setter. What does this mean? If there is no way to set a value, then the field is read only and we are trying to model an object with a
|
This issue doesn't seem like a duplicate of #12, as declaring that a property can be read, and that it can't be set are two very different things. When I recently tried to write an interface like this: interface HasTemplate {
get template(): HTMLTempalteElement;
} I wasn't trying to declare that This could be obviously implemented by a getter: class A implements HasTemplate {
private _template : HTMLTemplateElement;
get template() : HTMLTemplateElement { return _ template }
} but also by a field: class B implements HasTemplate {
template : HTMLTemplateElement;
}
class C implements HasTemplate {
readonly template : HTMLTemplateElement;
} Given that JavaScript has getters and setters, it seems like interfaces that describe JavaScript objects should include them. @RyanCavanaugh Any chance this suggestion could be reopened? |
I don't understand what the distinction is. What's an observable difference between a property with a getter but no setter and a readonly property ? |
The difference is between describing having at_least a getter, and describing not having a setter. Here's a table of interface declarations (columns) vs implementation (rows), and whether the implementation satisfies the interface (Note, I'm presuming that a readonly field in an interface is not satisfied by a read/write field in a class, I need to go check on that).
The problem with readonly would be if it's not satisfied by a field. When I put a getter in an interface I want to allow it be satisfied by fields or getters. |
A read/write property (whether implemented as a field or a get/set pair) is substitutable for a read-only property. |
@RyanCavanaugh that's... weird. If that's the case, then why was new syntax invented rather than just allowing standard accessors in interfaces? And why is the name of the annotation Anyway, I guess my main questions were answered. Thanks. |
If a read/write property weren't substitutable for a readonly property, the language would be literally unusable.
So that you can write this: class X {
readonly y: number;
}
I don't see why that name would be preferable. interface X {
readable a;
b;
}
let y: X;
let z = y.b; // must be illegal since 'b' wasn't marked readable
y.a = 1; // might be legal, 'readable' doesn't imply 'not writeable' |
I'm more referring to the fact that
JavaScript already has syntax for this though, it's: class X {
get y() { ... }
} Since I prefer my TypeScript to be as small of a difference from JavaScript as possible (TypeScript team's opinion may differ, of course), and that interfaces are classes without bodies/initializers, I would think that this would be preferable over an entirely new keyword: interface X {
get y();
} edit: I'm just bike shedding now... we don't have to spend time on this, unless you want to :) |
A getter can return an evaluated expression. class X {
readonly y: number;
} vs. class X {
get y (): number {
// return an evaluated expression
return 10 * 2 + myVar.jingle - 10; // etc.
};
} Of course you could move get y() into a function on the prototype, like getY,() but I am writing code to conform to a particular 3rd party API, so I need to use property accessors. +1 for the OP request. interface IEmployee{
get fullName: string;
set firstName: string;
set lastName: string;
} |
I hit this again when trying to write a declaration for a type that uses accessors, because I forgot that TypeScript doesn't support accessors in interfaces. I still this it's a usability/ergonomics issue, because the most obvious derivation of standard JavaScript syntax into TypeScript interfaces isn't used, but I realize now it's also a correctness issue. To reiterate the usability issue, when writing a declaration for this class: class X {
get y() { ... }
} I would expect to be able to just remove any method bodies and write: declare module 'foo' {
class X {
get y();
}
} This is the most obvious thing to try, yet it doesn't work. As for correctness, there is a difference in the shape of objects between a property and an accessor: class WithProperty {
x;
constructor(x) { this.x = x; }
}
class WithAccessors {
_x;
constructor(x) { this.x = x; }
get x() { return this._x; }
set x(v) { this._x = v; }
}
let withProperty = new WithProperty(1);
withProperty.hasOwnProperty('x'); // true
withProperty.__proto__.hasOwnProperty('x'); // false
let withAccessors = new WithAccessors(1);
withAccessors.hasOwnProperty('x'); // false
withAccessors.__proto__.hasOwnProperty('x'); // true So letting a class declaration describe it's accessors, which live on the prototype, separately from its fields/properties, which live on the instance would be the more correct solution. |
Right now interfaces use the same syntax for properties with and without accessors. This is fine for duck typed interfaces, but the problem is, there is no way to specify properties with only one part (getter or setter) present. Usually, they are read only, without setter. The problem is, we can't tell this to a compiler, so it will not warn us when assigning to such a prop and JS runtime will simply ignore this assignment without throwing.
It would be good to have syntax like this
to prevent such errors.
The text was updated successfully, but these errors were encountered: