Say you want to create a class to represent a point in two-dimensional space. If you come from a certain background, your immediate instinct may be to create some private properties and getter/setter methods:
|
This feels productive. You're writing code, after all! If you're feeling especially diligent, you might even write JSDoc comments for each of these methods. Your editor might even include shortcuts to write all these getters and setters for you.
Here's some code that uses that class:
|
The downside is that this is a lot of boilerplate code that doesn't do very much. Why write the getters and setters rather than this?
|
At least in Java, the answer is that getters and setters encapsulate the implementation of the class and give it much greater flexibility to evolve in the future.
For example, what if you realize that for some reason it's much better to use polar coordinates internally? With the getters and setters, it's no trouble to reimplement the old API using the new internal representation:
|
Users of the Point2D
class will be completely oblivious to this internal change. The code above works without change. Contrast this with DirectPoint2D
. You can't make an analogous change to this version because the internals are exposed. You can't get rid of the x
and y
properties without making a breaking change to the API. You're stuck.
That's the story in Java, anyway, and it was also the story for JavaScript in the 1990s and early 2000s. If you use very old JS libraries (or libraries written by recent transplants from Javaland), you may still run across these sorts of getter and setter methods. After publishing my post about Google's Closure Compiler I learned that one of its goals was to inline simple methods like these.
But getter and setter methods like these are not a good idea in modern JavaScript or TypeScript. The reason is that back in 2009, ES5 introduced a new syntax for get and set methods that entirely eliminates this problem with direct property access.
Here's how you'd migrate DirectPoint2D
to a polar coordinates representation using getter and setter methods:
|
Usage looks exactly as it did before:
|
What were direct property accesses before have become method calls. But the syntax is character-for-character identical, so the caller need not be aware that anything has changed. The public properties are no longer a constraint on your class design.
The takeaway here is that it's OK to use a public property on a class in JavaScript and TypeScript. You may read warnings in books and online about how this is a bad practice, but this advice has more to do with specific limitations of Java and old-school JS than it does with modern JavaScript and TypeScript. When you write getter and setter methods in JavaScript, you're working around a problem that no longer exists.
Simple getter and setter methods are a code smell in JavaScript and TypeScript. Don't write them! If you see them in a code review, suggest replacing them with a public property and send your coworker here for an explanation of why.
One cautionary note: don't go too crazy with get
and set
. When you read code like pt.x = 3
, you expect that this will do something like setting the x
property of pt
to 3
. Of course, with a set
method, it could do anything. It could set y
instead, or it could even issue a network request. But to avoid confusion and surprise, it's best if paired get
and set
methods get and set the same thing, at least conceptually.
Here's a complete playground link for the last example if you want to give it a try.