Determine the accurate data type while iterating through a for loop

I am facing an issue where I have around 40 unique actions defined, all with the same parameters except for each being provided with a different schema which is causing the problem

type ActionName = 'replaceText' | 'replaceImage';
type ActionTypes = 'element';
interface Action {
  objectId: string;
  name: ActionName;
}
interface Params<S> {
   element: {
      action: Action & S;
   }
}
const actions: Action[] = [{
  name: 'replaceImage',
  objectId: '123456'
}, {
  name: 'replaceText',
  objectId: 'replaceImage'
}];

class createAction<S, T extends ActionTypes> {
  run(params: Params<S>[T]) {
    console.log('paramas', params);
  }
}
type ReplaceImageSchema = {
  input: 'something'
}
type ReplaceTextSchema = {
  input: 'somethingElse'
}
const internalActions = {
  replaceImage: new createAction<ReplaceImageSchema, 'element'>(),
  replaceText: new createAction<ReplaceTextSchema, 'element'>()
}

for (const action of actions) {
  // the below fails because of conficting constituents
  internalActions[action.name].run({
    action
  })
  // the below works fine
  switch (action.name) {
    case 'replaceText': {
      const params = { action } as Params<ReplaceTextSchema>['element'];
      internalActions.replaceText.run(params)
    }
    break;
    case 'replaceText': {
      const params = { action } as Params<ReplaceImageSchema>['element'];
      internalActions.replaceImage.run(params)
    }
    break;
  }
}

Presently, I am using a switch statement with all available action names which is not ideal, hard to maintain, and involves repetitive code.

action.name contains the correct type with a list of available action names as string literals. Params takes a single generic of type Schema within the interface Schemas

Is there any way to solve this dynamically or is the switch statement the only solution?

It's important to mention that I have no control over which actions are available in actions in the given example. They are sent via a service.

Answer №1

In my opinion, the most efficient approach to tackle this problem is by utilizing a switch statement to thoroughly check all possible action types. However, I made an attempt to refactor the code in such a way that only one switch statement needs to be written. Below is the modified version:

type ReplaceTextAction = {
  name: 'replaceAction';
  objectId: string;
  input: string
}

type ReplaceImageAction = {
  name: 'replaceImage';
  objectId: string;
  input: string
}

type Actions = ReplaceTextAction | ReplaceImageAction

type Params = {
  element: {
    action: Actions
  }
}

class ActionHandler<T extends keyof Params> {
  async run(param: Params[T]) {
    const { name } = param.action
    switch(name) {
      case 'replaceAction':
      break;
      case 'replaceImage':
      break;
      default:
      throw new Error(`The specified action type is not handled; type = ${name}`)
    }
  }
}

const actions: Actions[] = [
  {
    name: 'replaceAction',
    input: 'something',
    objectId: '12345'
  },
  {
    name: 'replaceImage',
    input: 'somethingElse',
    objectId: '4321'
  }
]

const actionHandler = new ActionHandler()
for (const action of actions) {
  actionHandler.run({ action })
}

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

Utilizing ngFor to iterate over items within an Observable array serving as unique identifiers

Just starting out with Angular and I'm really impressed with its power so far. I'm using the angularfire2 library to fetch two separate lists from firebase (*.ts): this.list1= this.db.list("list1").valueChanges(); this.list2= this.db.list("list2 ...

Is it possible to exclude a certain prop from a styled component that has emotions?

Within my code, there exists a component called BoxWithAs, which is defined as follows: const BoxWithAs = styled.div( { WebkitFontSmoothing: 'antialiased', MozOsxFontSmoothing: 'grayscale' // And more … } ); Everythin ...

Error: Unable to retrieve options using this.getOptions function. This issue is unrelated to Vue, it is occurring within

Required Modules "dependencies": { "express": "^4.17.1", "express-static-gzip": "^2.1.1", "react": "^17.0.2", "react-dom": "^17.0.2", "reac ...

The styled component is not reflecting the specified theme

I have a suspicion that the CSS transition from my Theme is not being applied to a styled component wrapped in another function, but I can't pinpoint the exact reason. I obtained the Basic MUI Dashboard theme from this source and here. Initially, inte ...

What is the best way to define ngOptionValue for my ng-option selection?

After updating my select/option code to include a search feature, it caused an issue with my function create. Here is the HTML code: <div class="input-group"> <label htmlFor="categoria" class="sr-only"> ...

Angular2 is throwing an error: "NavigationService provider not found! (MenuComponent -> NavigationService)"

I am in the process of developing an angular (beta7) application. I aim to have a MenuComponent at the top that utilizes the NavigationService to navigate throughout different sections of my app. To ensure that the NavigationService remains a singleton, I ...

What is the best approach to implement server-side rendering in Next.js while utilizing Apollo React hooks for fetching data from the backend?

I have a Next.js project that is utilizing Apollo GraphQL to retrieve data from the backend. My goal is to render the page using server-side rendering. However, I am encountering an obstacle as the React hooks provided by GraphQL Apollo prevent me from cal ...

The value stored in Ionic Storage will only be visible on the HTML page after a refresh is performed

After updating my Ionic Storage values, they are not showing up on the HTML page until I reload it. I have researched similar issues faced by others, but the solutions I found either do not work or are no longer applicable due to changes in the Ionic versi ...

Tips for troubleshooting compile errors when updating an Angular project from version 6 to 7

I am currently working on upgrading my Angular 6 project to Angular 10, following the recommended approach of going through one major version at a time. Right now, I am in the process of updating it to version 7.3. Despite following the steps provided on u ...

Enhance your Angular app by dynamically adding classes to existing classes on a host component

How can I dynamically add a class to the host component of this angular component? @Component({ selector: 'test', templateUrl: './test.component.html', styleUrls: ['./test.component.scss'], encapsulation: ViewEncapsulation ...

The use of custom loaders alongside ts-node allows for more flexibility

Is it possible to utilize ts-node with a custom loader? The documentation only mentions enabling esm compatibility. ts-node --esm my-file.ts I am attempting to implement a custom loader for testing an ESM module, but I prefer not to rely on node for compi ...

Switching Facebook accounts on Firebase

I'm currently working on an Angular2 App that utilizes Firebase as its User system, with authentication providers including Email + Password, Facebook, and Google. One issue I have encountered is that when logging in with Facebook, I am unable to swi ...

A guide to confirm if an object includes an HTML element without compromising safety

When I implement a function that is triggered by a click event: useEffect(() => { document.addEventListener('click', (e) => handleClickOutside(e), true); }); The function itself: const myElement = useRef(null); const handleCli ...

What is the best way to ensure observables in a template (using async pipe) are fully subscribed to before executing any initialization code?

I am facing an issue with my HTTP service that returns information based on a given item ID. The data is fetched using a Subject, which receives the initial data in the ngOnInit method. To display the returned data in the HTML, I utilize the async pipe. ...

Avoid triggering onClick events on all rows when clicking, aim for only one row to be affected per click

I have a unique situation where I have a table containing rows with a button in each row. When this button is clicked, it triggers an onClick event that adds two additional buttons below the clicked button. The Issue: When I click on a button, the onClick ...

Revamping the static method signature of a class in Typescript

One of the modules I'm using is called some-module and it defines a class like this: export declare class Some<T> { ... static create<T>(): Some<T>; map<U>(x: U): Some<U>; } export default Some In my project, I ...

typescript error is not defined

While browsing online, I came across a post discussing how to transfer data from an MVC model to a .ts file. The suggestion was to include the following code: <script type="text/javascript"> var testUrl = @Html.Raw(Json.Encode(Model.testUrl) ...

What is the process for calculating a class property in typescript?

If I were writing this in JavaScript, it would look like: function a(b,c) {this.foo = b; this.bar = c; this.yep = b+c} // undefined b = new a(1,2) // a {foo: 1, bar: 2, yep: 3} However, I've been struggling to achieve the same in TypeScript. None of ...

The Battle of Identifiers: Named Functions against Anonymous Functions in TypeScript

When it comes to performance and performance alone, which option is superior? 1) function GameLoop() { // Performing complex calculations requestAnimationFrame(GameLoop); } requestAnimationFrame(GameLoop); 2) function GameLoop() { // ...

What is the best way to showcase the information retrieved from my API?

I am attempting to display the ID and Document number that are retrieved from an array. Data Returned However, I am not seeing any results in return. You can view the application results here. I have tried using string interpolation like {{document.id}} ...