interface IElement { state: string; temperature: number; warm: (degrees: number) => IElement; cool: (degrees: number) => IElement; deriveState: (temperature: number) => string; }; class WaterElement implements IElement { private _state = 'liquid'; private _temperature = 20; constructor(temperature: number = 20) { this._temperature = temperature; this._state = this.deriveState(temperature); } get state(): string { return this._state; } get temperature(): number { return this._temperature; } deriveState(temperature: number): string { if (temperature > 100) return 'gaseous'; if (temperature > 0 && temperature < 100) return 'liquid'; return 'solid'; } warm(degrees: number): IElement { const temperature = this.temperature + degrees; return new WaterElement(temperature); } cool(degrees: number): IElement { const temperature = this.temperature - degrees; return new WaterElement(temperature); } } const liquidWater = new WaterElement(20); const solidWater = liquidWater.cool(40); const steamWater = solidWater.warm(150); console.log(liquidWater.temperature, liquidWater.state); // 20 liquid console.log(solidWater.temperature, solidWater.state); // -20 solid console.log(steamWater.temperature, steamWater.state); // 130 gaseous function AlcoholElement(temperature: number = 20): IElement { const state = deriveState(temperature); function deriveState(temperature: number): string { if (temperature > 78) return 'gaseous'; if (temperature > -114 && temperature < 78) return 'liquid'; return 'solid'; }; function warm(degrees: number): IElement { const _temperature = temperature + degrees; return AlcoholElement(_temperature); } function cool(degrees: number): IElement { const _temperature = temperature - degrees; return AlcoholElement(_temperature); } const element = { deriveState, warm, cool }; Object.defineProperties(element, { temperature: { value: temperature, writable: false }, state: { value: state, writable: false } }); return element as IElement; } const liquidAlcohol = AlcoholElement(20); const solidAlcohol = liquidAlcohol.cool(240); const steamAlcohol = solidAlcohol.warm(450); console.log(liquidAlcohol.temperature, liquidAlcohol.state); // 20 liquid console.log(solidAlcohol.temperature, solidAlcohol.state); // -220 solid console.log(steamAlcohol.temperature, steamAlcohol.state); // 230 gaseous