Passing properties by reference in Typescript allows for the direct manipulation

In my Typescript class, I have four properties defined like so:

class MyClass {
     private x: number;
     private y: number;
     private z: number;
     private w: number;
}

I am looking to create individual functions that will increment each of these properties:

   incrementX() { this.x++; }
   incrementY() { this.y++; )
   ...

However, I want to avoid duplicating the increment logic (++) and instead consolidate it into a single function. If Typescript supported passing by reference like C#, I would handle it in this manner:

   incrementX() { this.increment(ref this.x); }
   increment(p: ref number) { p++; }

Since Typescript does not offer pass by reference, I have resorted to a less type-safe solution:

   incrementX() { this.increment("x"); }
   increment(p: string) {
       const self = this as any;
       self[p]++;
   }

This method is not completely type-safe. Despite adding a runtime check to ensure self[p] is a number, there is still room for error when calling increment('not-a-property'). Is there a more type-safe approach to tackle this issue?

Please note that although the example uses numbers, my actual code involves operations on a different class type.

Answer №1

One approach could be to use the combination of keyof and number extends. This way, you can restrict passing only keys that correspond to number data types in the class.

Check it out on the Playground!

class MyClass {
  public a: number = 0;
  public b: number = 0;
  public c: string = "";

  public increment(
     key: {
      [K in keyof MyClass]-?: number extends MyClass[K] ? K : never
    }[keyof MyClass]
  ) {
    this[key]++;
  }
}

const test = new MyClass();

test.increment("a");
test.increment("b");
test.increment("c"); // This will fail
test.increment("d"); // This will also fail

Answer №2

One possible approach is to specify p with the property names of MyClass.

     increment(p: keyof MyClass): void {
       this[p]++;
     }

However, this method has limitations as the number fields in MyClass are marked as private, and the function increment itself is in the keys.

To address this issue, another solution would be to isolate only the number type properties:

type OnlyNumberKeys<O> = {
  [K in keyof O]: O[K] extends number ? K : never;
}[keyof O];

This specialized type can then be utilized within the increment function:

class MyClass {
     x: number;
     y: number;
     z: number;
     w: number;

     increment(p: OnlyNumberKeys<MyClass>): void {
       this[p]++;
     }
}

Now, p will only allow values such as 'x' | 'y' | 'z' | 'x'.

It's important to note that the private keywords had to be removed in order for this workaround to function properly. There may not be a straightforward solution to retain privacy settings in this context.

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

Could React and GraphQL collaborate by sharing a query?

Is there a solution I might be missing? My experience with GraphQL is limited, so please forgive me if this question seems basic. I had a situation where I fetched data from GraphQL within a component, filtered it through a function, and then passed the f ...

The loop is returning a string element instead of the expected type from the array

I am facing an issue with looping through a TypeScript array. The following methods are being used: getNotification(evt: string, rowIndex: number) { console.log("Production order: law has changed to " + evt + " " + rowIndex); var select = document ...

Accessing the return value from an Angular subscription and storing it in

How can I use the value from a subscription to set the property for returning date and time? Component ngOnInit() { this.resetForm(); let defaultWIPEndTime = this.service.getDefaultWIPEndTime().subscribe(res => {}); console.log(defaultW ...

The Angular material slider experiences issues with functionality when paired with the *ngFor directive

Having a unique problem that I could easily replicate on stackblitz. When using multiple mat sliders generated from a *ngFor loop with numbers as values, encountering an issue where moving the first slider affects all others. Subsequent drags only update ...

Ways to check the functionality of the secondary tier in the api.send method

Currently, I am testing a function that involves returning a promise and subsequently calling the same function again at a second level. However, I am facing difficulties in accessing this second level of call. Below is the function code: itemToForm = () ...

Receiving an error when extending default functional component props in Typescript/React

I've been struggling with this issue for the past three hours and it's incredibly frustrating. Please forgive me if I come across as aggressive. All I want is to create a TypeScript interface for my component that extends the default interface u ...

Typescript's complex objects involve a sophisticated and intricate structure in

Just starting out with TypeScript and I'm looking to build a class that describes an object with the following structure: name(string), Array{ "column-name": { "type":"value", " ...

Upgrade to Angular 12: TypeScript is now an essential requirement for the Angular Compiler

Recently, I made sure to update my project to the latest Angular version. After running "ng update", I received a confirmation that everything was already up to date, indicating that all required packages had been successfully updated in the last step of t ...

Bring in Lambda layers on your local device

I've been trying to create a lambda function with a layer, but I'm stuck on how to get it running locally. Here's the current directory structure: - projectDir/ | - lambdas/ | | - match-puller/ | | | - scr/... | | | - index.ts | | ...

Can a dynamic import from a Node module be exported?

I have developed an npm package that utilizes a dynamic import(). This package is written in TypeScript and compiled with the module: "esnext" compiler option, which means the import() call remains unchanged in the output. The expectation was to load this ...

Check if the input values are already in the array and if not, then add

Within my React application, I am displaying an Array and each entry in the Array is accompanied by an input element. These input elements are assigned a name based on the entry's ID, allowing users to enter values. To handle the changes in these inp ...

An explanation on the differences between passing by reference and passing by value in a straightforward manner

In this code snippet, a variable of type TEST containing a static array is declared. The program then proceeds to call several functions by passing a pointer to this variable. typedef struct { char data[50]; } TEST; int main(int argc,char *argv[]) { ...

Issue encountered when attempting to deploy webpack react app to AWS S3 bucket

Hello, I've been using webpack to bundle my production code and everything works fine in the development environment. However, when I deploy to AWS S3 and try to access the website, I get a strange error: Uncaught ReferenceError: $RefreshReg$ is not d ...

Angular 16's platform-driven redirection functionality

I'm facing a challenge with my module that has its own subrouting. The page only consists of a header and material tabs linked to my routing, with the need for the landing tab to differ between desktop and mobile. To handle platform detection, I have ...

Collaborative React front end elements are now housed in their own TypeScript project within Visual Studio 2017

In Visual Studio, our project structure includes the following: PublicClient, Admin, and SharedComponents. The SharedComponents project is essential because many components are shared between our client and admin interface. This structure is based on the f ...

How can you vertically center an icon button in Material UI?

Looking for help with aligning elements in this code snippet: <TextField id="outlined-basic" label="22Keyword" defaultValue={"test123"} variant="outlined" /> <IconButton aria-label="delete&q ...

Checkbox in Angular FormGroup not triggering touched state

There seems to be an issue with the Angular form when checking if the form is touched, especially in relation to a checkbox element. Despite the value of the checkbox changing on click, I am seeing !newDeviceGroup.touched = true. I'm not quite sure wh ...

Issue potentially arising from TypeScript's type validation

Take a look at this code snippet: I defined a union type, but accidentally omitted one of the types from the type predicate. As a result, the function returned a value that was not a number, and no type error was detected during compilation: type Car = Sko ...

Challenges encountered when executing a node function in an AWS Lambda function

I have observed some unusual behavior with my AWS Lambda function. Here is the code snippet for the Lambda: import { TwitterApi } from 'twitter-api-v2'; const client = new TwitterApi({ appKey: 'APP_KEY', appSecret: 'APP_ ...

The issue of a mocked MobX store in Jest not resetting between tests is causing problems

I have a straightforward login component and a MobX store that holds user information. I am testing the integration using Jest. The application is built with Create React App, so my tests are based on that. This is what my Login component looks like: cons ...