What is the most effective method for caching a getter function (excluding decorators)?

Creating a getter cache can help optimize performance by storing and reusing the value rather than recalculating it each time. I am seeking the most efficient method to implement a getter cache without relying on decorators. While there are libraries that offer decorator solutions such as @cache, I prefer a more succinct approach with minimal typing.

Typescript playground

Here is an example:

type NoUndefined<T> = T extends undefined ? never : T;
function isNoUndefined <T> (value: T): value is NoUndefined<T> {
  return typeof value !== 'undefined'
}
function handleGetter <T>(value:T, operation: () => NoUndefined<T>): NoUndefined<T> {
  if (isNoUndefined(value)) return value;
  return operation()
}

class CalendarDate extends Date {
  #day: number | undefined = undefined
  get day () {
    return this.#day = handleGetter(this.#day, () => {
      console.log('runs once')
      return this.getDate()
    })
  }
}

const c = new CalendarDate()

c.day
c.day
c.day
c.day

Answer №1

To implement a more efficient method, you can create a function that updates the getter in an existing class prototype with a new one wrapped in a smart/self-overwriting/lazy approach:

function updateGetter<T>(ctor: { prototype: T }, property: keyof T): void {
  const descriptor = Object.getOwnPropertyDescriptor(ctor.prototype, property);
  if (!descriptor) throw new Error("PROPERTY DESCRIPTOR NOT FOUND");
  const oldGetter = descriptor.get;
  if (!oldGetter) throw new Error("GETTER NOT DETECTED");
  Object.defineProperty(ctor.prototype, property, {
    get() {
      const result = oldGetter.call(this);
      Object.defineProperty(this, property, { value: result });
      return result;
    }
  })
}

By using

updateGetter(Class, "key")
, it accesses the property descriptor for Class.prototype.key. If any step fails, an error is thrown. Otherwise, a new getter is created which calls the original getter once on the current instance and caches the value directly on the instance instead of the prototype. This optimizes subsequent accesses to the property.


Test this implementation by applying it after declaring the class:

class TaskDeadline extends Date {
  get dueDate() {
    console.log('executes once');
    return this.getDate();
  }
}
updateGetter(TaskDeadline, "dueDate"); // <-- here

Ensure its functionality:

const firstTask = new TaskDeadline()
console.log(firstTask.dueDate); // executes once, 25
console.log(firstTask.dueDate); // 25
console.log(firstTask.dueDate); // 25
console.log(firstTask.dueDate); // 25


const secondTask = new TaskDeadline();
secondTask.setDate(10);
console.log(secondTask.dueDate) // executes once, 10
console.log(firstTask.dueDate) // 25
console.log(secondTask.dueDate) // 10

Validation successful.


This method provides a concise solution for users who prefer avoiding decorators. The decorator alternative likely follows a similar logic, acting as a wrapper for the property descriptor.

Playground link to code

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The element in TS 7023 is implicitly assigned an 'any' type due to the fact that an expression of type 'any' is not valid for indexing in type '{}'

I have implemented a select-box that includes options, labels, optgroups, and values. Is my approach correct or is there something wrong with the way I have defined my types? interface Option { label: string value: string selected?:boolean mainGrou ...

The code executes smoothly on my local machine, but encounters an error on Heroku: [TypeError: An invalid character is present in the header content ["Authorization"]] {error code: 'ERR_INVALID_CHAR'}

I am currently working on a chatbot project that utilizes the openAI API to generate responses based on specific prompts related to a particular topic. Everything works perfectly when I test the code on my local machine. However, upon deploying it to Herok ...

An array that solely needs a single element to conform to a specific type

While I was pondering API design concepts, a thought crossed my mind. Is it feasible to define a type in this manner? type SpecialArray<Unique, Bland> = [...Bland[], Unique, ...Bland[]]; However, the error message "A rest element cannot follow anoth ...

Converting Http Client GET response into an array of objects with specified type

I have a model set up for the data I am retrieving through a GET request, and my goal is to map this data to an array of Player objects. This is an example of the response data received from the API: [ { Id: 1 PlayerName: "Dave", ...

Troubleshooting the issue with mocking the useTranslation function for i18n in JEST

Currently, I am facing an issue with my react component that utilizes translations from i18next. Despite trying to create tests for it using JEST, nothing seems to be getting translated. I attempted to mock the useTranslation function as shown below: cons ...

The module has defined the component locally, however, it has not been made available for

I have developed a collaborative module where I declared and exported the necessary component for use in other modules. import { NgModule } from '@angular/core'; import { CommonModule } from '@angular/common'; import { DateslideCompone ...

What is the reason for the component property being valued within the subscribe body when it is not defined outside of it?

I am currently facing an issue with retrieving data from a SQL server table using http.get within the constructor of an Angular component 5. While I am able to assign the retrieved data to a component property inside the subscribe method, the value becomes ...

Can a standard tuple be matched with its corresponding key?

This code snippet showcases a function that can recognize that the key "banana" cannot have the value "red": type Fruits = { banana: 'yellow' | 'green' strawberry: 'red' } const fruit = <K extends keyof Fruits>(modu ...

Enhancing TypeScript type definitions for the Response.render() method in Express

Struggling with enhancing the type safety of my Express project by extending the Response.render function. import { Response } from "express"; import { Product } from "../models/Product.interface"; export interface ProductListResponse ...

How can one assign a R6 object to a slot within an S4 object?

Looking for a solution to achieve the following: require("R6") R6cls <- R6::R6Class("R6obj", public = list(val = 1, foo = function() "foo!") ) # explore this R6 class r6o <- R6cls$new() r6o # <R6obj> # Public ...

Checking Validity of an Array of URLs with Class-Validator

I'm utilizing Class-Validator for validating DTO properties in my Nest.js application. One of the properties is "images", which is an array of strings representing URLs. I need to validate each URL in this array. class SomeDto { // ... // Arr ...

I'm experiencing a strange issue where my React component renders twice in production mode, despite having strict mode turned off. Can anyone advise me on

Within my layout.tsx, I have a structure that encloses the page with a container div and introduces a separately defined TopBar component alongside. The functionality seems fine, but an issue arises where the component is created before the {children}, as ...

Python class peculiar characteristics

Creating a class to establish a connection to a remote Linux Server using paramiko is essential. class Oracle: def __init__(self): ... self.outfile = outfile Within this class, there are 2 functions. The first function returns a list of files: def ...

Define an object by extracting properties from an array of objects

Is there a way to refactor this code to remove the need for explicit casting? type A={a:number}; type B={b:string}; let a:A={a:0}; let b:B={b:''}; function arrayToObject<T>(array:T[]):T { return array.reduce((r,c) =>Object.assign ...

Is it possible for a Firestore query using .where() to conduct a search with no results?

Currently, I am in the process of creating a platform where users can upload past exams. Each document contains various fields such as teacher, year, and class, all stored in cloud Firestore. To filter the data by teacher, I am using the .where("Teacher" ...

Issue with rendering classes in Next.js: Why is a class being inadvertently applied to a different element?

When one element should be displayed while the other should not, they are given classes based on their relationship with each other. View code There is a login button component with a border gradient, along with an avatar component. Login button componen ...

Conflicting prototypes of C++ classes

Below are two classes in separate header files that can appear in any order: //TestB.h class TestB; //Forward declaration for a later operator= in a centralised header class TestA { const TestA&operator=(const TestB); //defined in Test.h }; And: ...

Is it possible to dynamically assign class properties from an array in JavaScript/TypeScript?

Greetings for the assistance in advance. Currently, I am working with TypeScript, but I believe any JS solution will suffice. I aim to create something akin to the following: class ExcelData { 'Id 1': items[0].id, 'Quantity 1': item ...

Create a CoffeeScript file and export a class from it

When I'm referring to a CoffeeScript class from a separate file in my main script, the functions within the file can be made visible globally, but not the actual class itself. The external file looks like this: root = exports ? this root.add = (a, b ...

The natural elements are so minuscule they are practically nonexistent

When referencing my DOM element using ViewChild in the code, I sometimes encounter an issue where the offsetHeight and offsetWidth are zero in the ngAfterViewInit hook. This leads me to believe that the element has not yet been rendered in the DOM. Are the ...