Using Typescript to iterate through an array of objects and modifying their keys using the forEach method

I have an object called 'task' in my code:

const task = ref<Task>({
  name: '',
  description: '',
  type: undefined,
  level: 'tactic',
  participants: undefined,
  stages: undefined,
});

export interface Task extends CommonEntity {
  description?: string;
  type?: TaskType;
  level?: EntityLevel;
  participants?: number;
  stages?: TaskTypeStage[];
  questions?: TaskQuestion[];
  materials?: TaskMaterial[];
  indicators?: TaskIndicator[];
  duration?: number;
  images?: [];
  program_id?: number;
  roles?: TaskRole[];
  tables?: TaskTable[];
  competences?: TaskCompetence[];
  task_type_id?: number;
}

export interface CommonEntity {
  id?: number;
  name?: string;
  created_at?: string;
  updated_at?: string;
  errors?: ApiErrors;
  delete?: boolean;
  isTemporaryIdAdded?: boolean;
}

In a method, I need to modify the task object.

function handleSubmit() {
    task.value.materials = removeTemporaryIdFromArray<TaskMaterial>(task.value.materials);

    task.value.questions = removeTemporaryIdFromArray<TaskQuestion>(task.value.questions);

    task.value.roles = removeTemporaryIdFromArray<TaskRole>(task.value.roles);

    task.value.tables = removeTemporaryIdFromArray<TaskTable>(task.value.tables);
}

export function removeTemporaryIdFromArray<T>(
  entity: TaskMaterial[] | TaskQuestion[] | TaskRole[] | TaskTable[] | undefined
) {
  if (entity) {
    return entity
      .filter((item) => !item.delete || !item.isTemporaryIdAdded)
      .map((item) => Object.assign({}, removeTemporaryId<T>(item)));
  }
}

export function removeTemporaryId<T>(item: CommonEntity): T {
  const { id, isTemporaryIdAdded, ...rest } = item;

  return isTemporaryIdAdded ? (rest as T) : { id, ...(rest as T) };
}

I am looking for a way to optimize the handleSubmit function without repeating the same code for each key. How can I improve this?

I tried using a forEach loop with an array of keys, but the value returned is always 'undefined'. Can you suggest a better approach?

(['materials', 'questions', 'roles', 'tables'] as (keyof typeof task.value)[]).forEach((key) => {
      task.value[key] = removeTemporaryIdFromArray<typeof key>(task.value[key] as TaskMaterial[]) as undefined;
    });

I'm aware that my current solution is not ideal. Do you have any recommendations on how to improve it?

Answer №1

I believe the pretension is inappropriate

task.value[key] = removeTemporaryIdFromArray<typeof key>(task.value[key] as TaskMaterial[]) as undefined;

due to the fact that removeTemporaryIdFromArray cannot determine between multiple types (TaskMaterial[] | TaskQuestion[] | TaskRole[] | TaskTable[] | undefined) when setting task.value[key].

I suggest using removeTemporaryIdFromArray_InPlace instead.

Below is my suggestion (it might be a bit verbose), and testing it without context can be challenging. However, TypeScript validates it.

// helper type
type Writable<T> = { -readonly [K in keyof T]: T[K] };
// properties to iterate over
const KeyNames = ["materials", "questions", "roles", "tables"] as const;
// type helper with only the 'good' keys, never undefined (but we do not care.)
type SubPropertyTask = Required<Pick<Task, typeof KeyNames[number]>>;
// transforming the properties into an array of keys for easy iteration
const WriteableKeyNames = KeyNames as Writable<typeof KeyNames> as Array<keyof SubPropertyTask>;

// new submission function
function handleSubmitV3() {
    // iterating over keys
    WriteableKeyNames.forEach((key: keyof SubPropertyTask) => {
        // calling the somewhat non-trivial function
        // < type of first element of the array (because we know its array)
        // also need to specify the type of the key, which cannot be inferred.
        removeTemporaryIdFromArrayInplace<SubPropertyTask[typeof key][0], typeof key>(task[key], key);
    });
}
// T might be written like this : T extends TaskQuestion | TaskMaterial | TaskRole | TaskTable
// but if you need to add other property, name should be enougth
export const removeTemporaryIdFromArrayInplace = <T, E extends keyof SubPropertyTask>(entity: Array<T> | undefined, e: E): void => {
    // custom filtering
    const filteredEntity = entity?.filter((taskObject: T) => taskObject);

    // assigning the new value, with the necessary cast
    // Because at this time typeof task[e] is same as T and as  SubPropertyTask[E] | undefined
    // ... I think.
    task[e] = filteredEntity as SubPropertyTask[E] | undefined;
};

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

Navigating to a specific tab within the Bootstrap-vue tabs while using vue routes

I have implemented tabs using Bootstrap-vue in my project. The HTML code for the tabs is as follows: <b-tabs> <b-tab title="Exotic Dogs" href="#dogs"> <br>Content for Dogs tab here </b-tab> <b-tab title="Exotic Cats" h ...

There was an issue encountered while trying to install Electron using the command 'npm install electron'

While attempting to install electron using npm install electron, I encountered the following error: 908 error code 1 909 error path C:\Users\name\Documents\name\Code\code\node_modules\gl 910 error command failed ... ...

Guide to extracting the key of a JSON object with a numerical name

I am having trouble extracting JSON objects from my server that contain numbered names to distinguish them. When trying to retrieve these objects, I encounter an issue with appending numbers to the common name. The structure of the objects is as follows: ...

The term "this" in the global scope versus within a function in the Node.js environment

console.log(this) // {} function abc(){ console.log(this) } abc() // global The concept of this can vary between the global space and inside a function in Node.js. console.log(this === module.exports) // true function abc(){ ...

Generating intricate JSON structures with JavaScript programming instructions

In the world of JSON, I am still a novice. I need to use JavaScript to construct the JSON structure below, but I'm struggling with adding the second element ("12101") and populating the people in the JSON Structure. Below is the code I tried, however, ...

Tips for setting up OpenLayers demonstrations on a personal server?

Currently, I'm working with an older version of OpenLayers (4.6.2) and find the provided examples extremely helpful for testing and reference purposes. However, the official web page with updated examples only includes the latest version. I am wonder ...

What is the best way to deliver HTML and JavaScript content using Node.js from virtual memory?

Situation I'm currently developing an in-browser HTML/JS editor, utilizing memory-fs (virtual memory) with Webpack and webpack-html-plugin to package the files created by users in the editor. Storing the files in virtual memory helps avoid I/O operat ...

"Utilizing jQuery's bind method with IE 7 compatibility and the use of

One of the scripts I'm working with is a treeview script, and a portion of it appears like this: root.find("." + classControl).each(function () { $(this).bind('click', function () { if ($(this).text() == "-") { $(thi ...

Guide me on setting up a progress bar using the XHR function to display the upload percentage

let formData2 = new FormData(); formData2.append('_token', vm.response._token); formData2.append('file', vm.response.content[i].path); formData2.append('type', vm.res ...

Utilizing Node Js for Form Data Transmission and Retrieval

I've successfully created two separate JS files, each responsible for sending and retrieving form data to and from the database. My dilemma is whether it's more practical or feasible to leave these two JS files as they are and serve their individ ...

When compiling my TypeScript file, I encountered an error stating that a block-scoped variable cannot be redeclared

In my Visual Studio Code, I have written just one line of code in my ex1.ts file: let n: number = 10; Upon compiling using the command tsc ex1.ts, the compiler successfully generates the ex1.js file. However, VSC promptly displays an error in the .ts file ...

Generate a complete screenshot of a website using Chrome 59 through code

Is it possible to programmatically capture a full-page screenshot of a website using the latest Chrome 59 and chromedriver with Selenium Webdriver, even if the website is larger than the screen size? ...

Choosing multiple items using ng-checked and ng-model in AngularJS

I am working with an Ionic application and encountering a minor issue, much like AngularJS. <ion-list class="list-inset subcategory" ng-repeat="item in shops"> <ion-checkbox class="item item-divider item-checkbox-right" ng-model="selectAll" ...

What is the process for implementing pagination in vue-tables-2 with a Laravel REST API?

I'm looking to implement pagination on Vue server-table using a Laravel endpoint. How can I achieve this? Below is my component setup: <template> <div> <v-server-table :columns="columns" url="/object/find" :options="option ...

Encountering Issue: Exceeding the number of hooks rendered in the previous render cycle

Every time I load my Nextjs page, an error message displays: "Error: Rendered more hooks than during the previous render." I attempted to fix this by adding if (!router.isReady) return null after the useEffect code. However, this caused a problem where th ...

The error message "The type 'DynamicModule' from Nest.js cannot be assigned to the type 'ForwardReference' within the nest-modules/mailer" was encountered during development

Recently, I decided to enhance my Nest.js application by integrating the MailerModule. I thought of using the helpful guide provided at this link: Acting on this idea, I went ahead and performed the following steps: To start with, I executed the command ...

Issues with Javascript functionality on aspdotnetstorefront site

Lately, I've been facing some challenges with my Javascript and jQuery codes when trying to implement them in the storefront. Despite attempting different methods to create a slider, none seem to work within the store environment - even though they fu ...

Looking to trigger the closing of a q-popup-proxy by clicking a button from a separate component

I am currently working with a q-popup-proxy component in my project. <q-btn label="Add Fault" class="addFaultButton" dense @click="openPopUp()"> <q-popup-proxy position="center" v-if="openFaults" ...

What is the process for assigning variables to modules using RequireJS?

Is there a way to define variables for modules in RequireJS? In simpler terms, how can I achieve the equivalent of the following using RequireJS: var fs = require('fs'); var child_process = require('child_process'); I am looking to s ...

Instead of relying on Vue TypeScript, we are leveraging IntelliJ with TypeScript 5.0.3 to compile our Vue project

My current version of IntelliJ IDEA is 2023.1 (Ultimate Edition) Build #IU-231.8109.175, released on March 28, 2023. I am facing an issue where my project fails to compile using "Vue TypeScript", resulting in some type mismatches being overlooked. In the ...