A not so popular class pattern - TypeScript
By Harsh Rohila
- 3 minutes read - 582 wordsTypeScript is a flexible language. Using it you can write code doing the same thing in multiple ways. This can be considered as both disadvantage and advantage of the language.
Disadvantage being, people need to know so many things to understand variety of codebases.
I consider this as a good thing, as it offers developers so many ways to express what code is doing. Developers can use that to make code more readable according to different situations.
This post is to share one of the patterns I am using for months now for writing code in Object-Oriented way.
Let’s first look at the most popular way to define class
class Pattern {
#name: string
constructor(name: string) {
this.#name = name
}
displayName() {
console.log(this.#name)
}
}
If you are seeing # first time. It is the newly introduced, most ugly syntax in the language to use private properties and methods
This class can be used like this
const popularPattern = new Pattern('Popular')
popularPattern.displayName()
Now let’s look at the code doing the same thing differently
const Pattern = {
create(name: string) {
function displayName() {
console.log(name)
}
const publicAPI = {
displayName
}
return publicAPI
}
}
This can be used as
const notPopularPattern = Pattern.create('Not Popular')
notPopularPattern.displayName()
This may look weird at first. But now you can easily recognize this pattern in codebases.
This is using a Factory function(create) to create objects
For readers of the book “You don’t know JS Yet”. This is inspired by the example given in the book with a little variation of using a top level
const
. With this variation we can directly use functions in other modules without renaming them at imports. Also, it allows easy mocking (see pros below)
Pros of using this way
this
keyword is not used.this
keyword in JavaScript, as you maybe already knowing, is according to context. No need to worry aboutthis
is not having correct value.Fewer concepts used. Not using keywords like
class
,this
,#
,constructor
You can keep the function name and definition separate. So you can keep all the functions in an object(here
publicAPI
) object to quickly know what is public.Easy mocking. If you are using
Jest
, it is as simple as
Pattern.create = jest.fn()
Cons
- Memory consumption - In Factory way, each object having its own set of public functions. Whereas in
class
way, public methods are shared between objects. So factory way takes more memory, but this only becomes significant if we create thousands of the same object. Reference
Let’s look at a class example with more concepts to know how they can be mapped to Factory pattern
class Pattern {
static displayClassName() {
console.log('Pattern')
}
#name: string
constructor(name: string) {
this.#name = name
}
#privateDisplayName() {
console.log(this.#name)
}
displayName() {
this.#privateDisplayName()
}
}
I added static method and a private function
Factory way can look like
const Pattern = {
displayClassName() {
console.log('Pattern')
},
create(name: string) {
function privateDisplayName() {
console.log(name)
}
function displayName() {
privateDisplayName()
}
const publicAPI = {
displayName
}
return publicAPI
}
}
Using static method is same in both patterns
Pattern.displayClassName()
Bonus
Not so popular export
pattern
I noticed most code bases use export at the end, or export individual things by using export for each thing
I like to keep export at the top, just below the imports, to easily know the module’s public API without scrolling down to the bottom of the file
import { Magic, doMagic } from 'some-magic'
export { MyMagic }