I learnt typescript in a hurry and still a newbie. Coming from a functional background I am used to seperate object data from its methods. For example let us consider a simple data object that i will want to augment as my code base grow :
export interface Data {
readonly value: number;
}
export function Data(value: number): Data {
return { value };
}
const data: Data = Data(2);
console.info(data); //output { value : 2 }
Data is a central brick of my code since it contains all the data defining a core object of my business model. My application growing in feature I want to augment it with dedicated methods.
I thought of two approaches.
I can use the “companion object” pattern :
export namespace Data {
export function incr(self: Data): Data {
return Data(self.value + 1);
}
}
console.info(data); //output { value : 2 }
Data.incr(data);
With this approach data stay a simple data object, and I can enrich it. However, each time I will use it I will have a bloated syntax
I can use the interface pattern :
export namespace Data {
export function Ops(self: Data) {
return {
self : self,
incr(): Data(self.value + 1);
}
}
}
const data_withops = Data.Ops({ value : 2 });
console.info(data_withops.self); //output { value : 2 }
data_withops.incr()
It is less bloated but I still have to manually encapsulate/decapsulate the data.
I can inject the operators into the data
export interface Data {
readonly value: number,
incr(): Data
}
const data: Data = Data(2);
console.info(data); //output { value : 2, incr : Function }
But then the object is bloated, bloating my console, my cache and my network.
Is it possible to do it without any of those problems?
- data is not bloated : console.info(data); //output { value : 2 }
- user of the object does not have to care about “loading/unloading” the operators
- user has a light syntax : “data.incr()” is defined
I thought it would works with the prototypes but I didn’t succeed
declare global {
export interface Data {
incr(this: Data): number;
}
}
Data.prototype.incr = function (this: Data): Data {
return Data(this.value + 1);
};
console.info(data); //output { value : 2 }
data.incr();