Ways to transfer specific properties from one object to another in TypeScript

I'm currently working on a function that selectively copies key-value pairs from one object to another in order to remove certain properties. The code snippet for this function is shown below:

sanitizeData: function (sourceObject: object, ...allowedKeys: string[]) {
    const sanitizedObject = {};
    Object.keys(sourceObject).forEach((key: string) => {
      if (allowedKeys.includes(key)) {
        sanitizedObject[key] = sourceObject[key];
      }
    });
  }

The purpose of this function is to take an object (e.g., req.body) and a list of strings as input, then perform the specified operation. For instance, an input may look like:

sanitizeData(req.body, 'name', 'age', 'email')
.

This code works seamlessly in JavaScript, however, when trying to run it in TypeScript, I encounter the following error message:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
  No index signature with a parameter of type 'string' was found on type '{}'.

If anyone knows how to resolve this TypeScript error, your help would be greatly appreciated.

Answer №1

Here is an innovative solution that intelligently chooses the keys you provide and upholds the typings in the output.

// To ensure that the object you pass extends a record with string keys,
// we use any as a workaround for now, there might be a better way to do this
function sanitize<T extends Record<string, any>, K extends keyof T>(obj: T, ...keys: Array<K>): Pick<T, K> {

  // We implement your code in a different manner while maintaining its functionality
  return Object.entries(obj).reduce((a, [k, v]) => {

    // Here, I opted for find due to the limitation of includes accepting only strings
    if (keys.find((a) => a === k)) {
      a[k as K] = v;
    }
    return a;
  }, {} as Pick<T, K>);
}

// Testing the function with sample data should yield the expected results.
const test = {
  a: 1,
  b: 4,
  c: 6
};

const output_a = sanitize(test, 'a', 'b');

// This will generate a type error
output_a.c;
// No type error will occur here
output_a.a;

console.log(output_a);

It's important to note that even though generics are defined within the function, they don't need to be explicitly specified during invocation when the arguments provide enough context. Thus, the generics are implicitly inferred.

Check out my playground.

Answer №2

If you're looking to maximize code reuse, here's a TypeScript snippet that can help:

const allowed: Array<string> = ['key1', 'key3']
const body = {
  'key1': 1,
  'key2': 2,
  'key3': 3
}

function sanitizeBody (obj: Record<string, any>, allowed: Array<string>): Record<string, any> {
  const sanitizedObject: Record<string, any> = {};
  Object.keys(obj).forEach((el: string) => {
    if (allowed.includes(el)) {
      sanitizedObject[el] = obj[el];
    }
  })
  return sanitizedObject
}

const output = sanitizeBody(body, allowed)

The resulting output will be:

{
  "key1": 1,
  "key3": 3
} 

Ensuring that you provide TypeScript with the correct types will allow it to work seamlessly as intended.

Answer №3

Understanding Index Signatures in TypeScript

When working with typescript, it is essential to grasp the concept of index signatures. These are used when the properties' names are unknown but we know the shape of their values. By providing an index signature, we can define the values and specify the type of index associated with them.

If typescript is unable to interpret the index signature for an object type like in your case, it becomes necessary to explicitly define it. Here is a common approach to defining the index signature:

type IndexSignature = {
  [key: string]: string; //in this example, the value is a string
}

Once you have defined the IndexSignature, adjustments need to be made within your codebase. Below is an example of how you can modify a function utilizing the IndexSignature:

sanitize function(obj: IndexSignature, ...allowed: string[]) {
    const sanitizedObject = {} as IndexSignature;
    Object.keys(obj).forEach((el: string) => {
      if (allowed.includes(el)) {
        sanitizedObject[el] = obj[el];
      }
    });
  }

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

Utilize only the necessary components from firebase-admin in Cloud Functions with Typescript

When I looked at my transpiled code from cloud functions, I noticed the following TypeScript import: import { auth, firestore } from 'firebase-admin'; It gets transpiled to: const firebase_admin_1 = require("firebase-admin"); Upon further exa ...

Issue: Execution terminated with error code 1: ts-node --compiler-params {"module":"CommonJS"} prisma/seed.ts

Whenever I run npx prisma db seed, I encounter the following error: Error message: 'MODULE_NOT_FOUND', requireStack: [ '/run/media/.../myapp/prisma/imaginaryUncacheableRequireResolveScript' ] } An error occurred during the execution ...

When the first argument is missing, using a recursive constraint default can result in the incorrect inference of the second argument, especially when both arguments share the same generic

Currently, I am developing a TypeScript implementation of a recursive binary search tree (BST) data structure using generic constraints. In order to establish a default for the recursive generic variable T without directly using it in the default declarati ...

Discover the process of implementing nested service calls in Angular 2 by utilizing observables

Here are my component file and service file. I am trying to achieve that after the verification() service method is successfully called, I want to trigger another service method signup() within its success callback inside subscribe. However, I am encounter ...

Enhancing the default functionality of React.FC within Next.js

Currently, I am working on a tutorial in Nextjs that employs the code snippet below in JavaScript. However, I am planning to transition it to TypeScript. Since I am relatively new to TypeScript, I have attempted various solutions from different sources but ...

What are the guidelines for utilizing square brackets [ ] in directives like @Inputs?

I'm feeling a bit lost. Check out this straightforward directive: @Directive({ selector: '[myDirective]' }) export class MyDirective { private textContent: string; private isEnabled: boolean; @Input() myD ...

Create a Referral Program page within a swapping interface similar to the functionalities found in platforms like PancakeSwap, PantherSwap, and

Currently, my goal is to create a referral program page similar to the one found at . I have explored the source code on GitHub for the PantherSwap interface, but unfortunately, I did not come across any references to that specific section. Would you be ...

Clicking a button in React requires two clicks to update a boolean state by triggering the onClick event

I have a React functional component with input fields, a button, and a tooltip. The tooltip is initially disabled and should only be enabled and visible when the button is clicked and the input fields contain invalid values. The issue I'm facing is t ...

Compile a roster of service providers specializing in unit testing imports

Recently joining a new team that works with Angular, they asked me to implement unit testing on an existing project built with Angular 8. After researching the best approach, I decided to use Karma + Jasmine for testing. I set up a .spect.ts file structure ...

Allowing cross-origin resource sharing (CORS) in .NET Core Web API and Angular 6

Currently, I am facing an issue with my HTTP POST request from Angular 6. The request is successfully hitting the .net core Web API endpoint, but unfortunately, I am not receiving the expected response back in Angular 6. To make matters worse, when checkin ...

Tips for isolating data on the current page:

Currently, I am using the igx-grid component. When retrieving all data in one call and filtering while on the 3rd page, it seems to search through the entire dataset and then automatically goes back to "Page 1". Is there a way to filter data only within th ...

Using optional chaining on the left side in JavaScript is a convenient feature

Can the optional chaining operator be used on the left side of an assignment (=) in JavaScript? const building = {} building?.floor?.apartment?.number = 3; // Is this functionality supported? ...

Tips for displaying dynamic images using the combination of the base URL and file name in an *ngFor loop

I have a base URL which is http://www.example.com, and the file names are coming from an API stored in the dataSource array as shown below: [ { "bid": "2", "bnam": "ChickenChilli", "adds": "nsnnsnw, nnsnsnsn", "pdap": " ...

Preserve the checkbox state upon refreshing the page

I am facing an issue with keeping the checkbox state saved after a page reload. Currently, I have stored my unchecked checkboxes in localStorage, but I am unsure about what steps to take next. In simple terms, I want the checkbox to remain unchecked when I ...

What is the correct way to handle the return value of an useAsyncData function in Nuxt 3?

How can I display the retrieved 'data' from a useAsyncData function that fetches information from a pinia store? <script setup lang="ts"> import { useSale } from "~/stores/sale"; const saleStore = useSale(); const { da ...

Icon for closing Mui Snackbar

I am facing an issue with my notification component that uses the mui snackbar to display alerts. I want to display multiple notifications stacked vertically, but when I try to close one notification using the "Close" icon, it ends up closing both that o ...

SonarQube alerting you to "Eliminate this unnecessary casting"

Can someone help me understand why SonarQube is flagging this error and suggest a resolution? The unnecessary cast should be removed. Promise.all([ this.customerViewCmr4tProvider.getData(activeNumber), this.customerBillManagementProvider.getData(ind ...

Is there a method to improve type inference in vscode?

Recently, I created a component with a click handler that looks like this: onClick={(e) => { e.stopPropagation(); }} It seems clear to me, but then the Typescript compiler complains that it's not a valid signature for onClick, which actually a ...

Using React with Typescript: Can the parent component access data fetched from a nested child component?

Can I access data retrieved from a child component (using a graphql query) in a parent component? For example, how can I use the data fetched by React-component-4 in React-component-1? Is there a way to do this or do I have to duplicate the data fetching ...

What is the best way to retrieve a property value from an object using the .find() method?

I've encountered a problem with the following code snippet in my function: let packName: string = respPack.find(a => {a.id == 'name_input'}).answer.replace(/ /,'_'); My goal is to locate an object by matching its id and retrie ...