Description
This is a feature request. I think it's time to revisit getters and setters, originally discussed in #4915, and explicitly unsupported on https://coffeescript.org/#unsupported-get-set
Input Code
class Foo
get random: -> Math.random()
get name: -> Db.query 'name'
set name: (newName) -> Db.update 'name', newName
Expected Behavior
class Foo {
get random() {
return Math.random();
}
get name() {
return Db.query('name');
}
set name(newName) {
Db.update('name', newName);
}
}
Current Behavior
error: 'get' cannot be used as a keyword, or as a function call without parentheses
Motivation
I have two arguments for why getters and setters should be added to CoffeeScript.
One argument is increased recommended usage. Previously @GeoffreyBooth argued that getters/setters seemed likely useful in reactive JavaScript libraries, but "my worry is hypothetical, so maybe it’s not worth worrying about until there’s a concrete example". Now a concrete example is SolidJS, an increasingly popular reactive library for building user interfaces, which uses getters/setters extensively as a way to detect reactivity. For example, components get a props
argument, and use of props.name
in e.g. <h1>Hello {props.name}</h1>
automatically reacts to changes to the name
property. In the case of props
, that's all done by SolidJS automatically, but the SolidJS documentation encourages using getters extensively (try searching the page for get
including the space). Specifically, in order to build your own reactive objects that work just like the built-in ones, you build an object with getters and/or setters. In SolidJS, use of reactive data needs to be wrapped in a closure for automatic reactivity to work. For single states, this is done via a "getter" function (a function that takes no arguments), which is fine in CS; but for complex state with multiple attributes, it's done by building an object with multiple getters.
(Incidentally, other than this issue, writing SolidJS apps in CoffeeScript works well. In fact, early versions of SolidJS were written in CoffeeScript! But before the move to getters/setters.)
The second argument is better TypeScript support (for #5307). The recommended workaround for getters and setters in CoffeeScript is to use Object.defineProperty
. But TypeScript doesn't understand the effect of Object.defineProperty
. It's not even possible to provide an explicit type because a single assignment to the variable must provide all the properties at once, and Object.defineProperty
can only do one at a time and only after the object has been initialized.
Context
Conveniently, CS2 already made get foo: ->
syntax invalid, and similarly for set
. (Actually, get foo: ->
alone is valid, but it becomes invalid if you add a blank line before it. This is presumably a bug.) It was reserved for this very purpose: get
/set
became useful enough to add (and to prevent confusion if someone guessed at the correct notation). So, we can use the obvious syntax and all CS2 code remains compatible.
A get
/set
prefix would also go well with private
, protected
, and public
annotation prefixes, which are TypeScript features. (Note that private
, protected
, and public
are reserved words in CoffeeScript, so they could be added while being backward incompatible. It's a separate discussion whether they are worth adding.)
Alternatives
If the ambiguity with calling functions get
or set
is considered too confusing, we could consider a new syntax that is not ambiguous. For example, inspired by ideas in #5367, we could add a :
prefix:
class Foo
:get random: -> Math.random()
:get name: -> Db.query 'name'
:set name: (newName) -> Db.update 'name', newName
I personally find this uglier, so would prefer straight get prop:
/set prop:
syntax.