Is it feasible to securely remove an item from an array within an object without the need for any assertions in a single function?

My interest in this matter stems from curiosity. The title may be a bit complex, so let's simplify it with an example:

type ObjType = { items: Array<{ id: number }>; sth: number };
const obj: ObjType = {
  sth: 3,
  items: [{ id: 1 }, { id: 2 }, { id: 3 }]
};

Suppose I want to delete an item by its id from obj.items, for instance the id 2, and return the updated object without mutating the original one. Achieving this in JavaScript is straightforward, but I aim to maintain type safety using TypeScript as well. Here is what I came up with:

function removeItemFromArray<S, A extends { id: number }>(
  obj: S,
  field: keyof S,
  array: A[],
  valueToCompare: number
): S {
  return {
    ...obj,
    [field]: array.filter(i => i.id !== valueToCompare)
  };
}

The presence of the array parameter might seem redundant since it will always refer to obj[field]. However, if we were to simply use obj[field], there would be no guarantee that it actually represents an array. So, how can we ensure that obj[field] indeed contains an array of objects with an id property? My intuition tells me that conditional types hold the key to solving this puzzle, though I'm still working on unraveling the specifics.

Answer №1

To achieve the desired outcome, it is important to ensure that the obj object has the correct structure:

function removeFromArray<S extends { [f in F]: { id: number }[] }, F extends keyof S>(
  obj: S,
  field: F,
  valueToRemove: number
): S {
  return {
    ...obj,
    [field]: obj[field].filter(i => i.id !== valueToRemove)
  };
}

const obj = {
  something: 3,
  elements: [{ id: 1 }, { id: 2 }, { id: 3 }]
};

removeFromArray(obj, "elements", 2);

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 error message "Type 'IPromise<{}>' is not compatible with type 'IPromise<TemplatesPagingModel>' in typescript version 2.8.0" is displayed

Currently, I am working on an AngularJS framework (version 1.5.8) with the latest TypeScript files (version 2.8.0). However, after updating to the most recent TypeScript version, the code below is not compiling. Implementation of Angular interface: inter ...

Exploring Angular2 Heroes Guide - Declaring Hero Properties with Nested Objects

Currently, I am diving into the Angular2 Tour of Heroes guide and striving to grasp the concept of services. So far, I've successfully implemented the basic tutorial, but as I attempt to add more complexity, my application crashes without clear reason ...

Developing a versatile table component for integration

My frontend app heavily utilizes tables in its components, so I decided to create a generic component for tables. Initially, I defined a model for each cell within the table: export class MemberTable { public content: string; public type: string; // ...

Revamping an npm package on GitHub

Currently, I am managing a project that has gained popularity among users and has received contributions from multiple individuals. The next step I want to take is to convert the entire library into TypeScript, but I am unsure of the best approach to ach ...

Exploring the Capabilities of TypeScript 1.8 in Visual Studio 2017

Recently, I've encountered an issue with my Visual Studio project that was created using TypeScript 1.8 in Visual Studio 2015. Upon upgrading to Visual Studio 2017 and attempting to open the project in the new IDE, I noticed that the TypeScript versio ...

Mastering Inter-Composable Communication in Vue 3: A Guide

Composables in Vue documentation demonstrate how small composition functions can be used for organizing code by composing the app. Discover More About Extracting Composables for Code Organization "Extracted composables act as component-scoped servi ...

Utilizing getter and setter functions within a setter with a type guard

I need to implement a getter and setter in my class. The setter should accept a querySelector, while the getter is expected to return a new type called pageSections. The challenge I'm facing is that both the getter and setter must have the same argum ...

How to verify if an unknown-type variable in TypeScript contains a specific property

When using typescript with relay, the props passed down are of type unknown. Despite my efforts, I am unable to persuade the compiler that it can have some properties without encountering an error: <QueryRenderer environment={environment} query={te ...

Guide on incorporating a bespoke cordova plugin into your Ionic 4 project

After successfully completing all the necessary steps to create a new Cordova plugin as outlined in the link below: Start android activity from cordova plugin I executed the command cordova plugin ls which returned the following result: com.example.sam ...

Experimenting with Cesium using Jasmine (Angular TypeScript)

I have a TypeScript app built using Angular that incorporates Cesium: cesium-container.component.ts import { Component, ElementRef } from '@angular/core'; import { Viewer } from 'cesium'; import { SomeOtherCesiumService } from 'sr ...

Webpack and React.js: Additional loaders might be required to manage the output generated by these loaders

An error occurred while parsing the module in ./productFlow/index.tsx at line 3, column 12. The file was processed with the following loaders: * ./node_modules/awesome-typescript-loader/dist/entry.js. It seems like an additional loader may be needed to h ...

Creating hierarchical TreeNode structure in TypeScript

As I work with a flat one-dimensional array of type TreeNode (view interface definition below), my goal is to recursively traverse the array and add subsequent array elements as children. While attempting a non-recursive approach using a buffer, I encount ...

What is the best way to refresh existing data retrieved by React Query without having to fetch everything again?

My current code structure requires me to refetch all the data after a successful mutation, as the client-side tasks are not updated automatically. Is there a way to update the tasks directly when I create or delete a task? const { data: sessionData } = ...

The issue with dispatching actions in TypeScript when using Redux-thunk

As a beginner in TypeScript, I apologize if my question seems silly, but I'll ask anyway: I'm attempting to make an async call getUsersList(), but the issue is that it's not triggering the dispatch (it's not logging "hello"). It worked ...

The specified file cannot be found within the Node file system

I am working on a project that involves reading a file using Node fs, and I have the following code: const files: FileSystemTree = { "Component.tsx": { file: { contents: fs.readFileSync( `../../apps/components ...

What is the best way to destructure a blend of React props and my own custom props in my code?

I have a requirement to develop a custom React component that serves as a wrapper for the Route component in order to create secure routes. The challenge I am facing is how to access the element property, which is typically specified in the <Route> e ...

How to transfer the label text value from html to .ts file in Ionic 3?

Hey everyone! I just started using Ionic and I'm wondering how to pass the value of a label text from HTML to the .ts file. Here's a snippet of my code: <div class="box" (click)="openChatBot()"></div> <ion-label>LEADER ...

What steps do I need to take for webpack to locate angular modules?

I'm currently in the process of setting up a basic application using Angular 1 alongside Typescript 2 and Webpack. Everything runs smoothly until I attempt to incorporate an external module, such as angular-ui-router. An error consistently arises ind ...

Utilizing a function within the catchError method

A function has been defined to handle errors by opening a MatSnackBar to display the error message whenever a get request encounters an error. handleError(err: HttpErrorResponse) { this.snackBar.open(err.message) return throwError(err) } When subs ...

What exactly does "nothing" mean in Node when using async await?

I have a method as shown below: private async sendToAll(clients) { for(const client of clients) { this.send(client, message); await true; // What should I put here to allow the rest of the application to continue executi ...