Delete an essential attribute from an entity

I am trying to remove a required property called hash from an object, but I keep encountering TypeScript or ESLint errors. All the properties of the interface are mandatory, and I do not want to make all properties optional using Partial.

Here is the interface for my object:

interface UserRecord {
  name: string;
  age: number;
  hash: string;
  id: string;
}

Approach 1: Using Omit

// User object comes from a response
const userObj: Omit<UserRecord, 'hash'> = user;
delete userObj.hash;

Issue:

The error "Property 'hash' does not exist on type 'Omit'" appears.

Approach 2: Using Exclude

const userObj: Exclude<UserRecord, 'hash'> = user;
delete userObj.hash;

Issue:

An error stating "The operand of a 'delete' operator must be optional" occurs.

Approach 3: Spreading object and ignoring the property

const { hash, ...userWithoutHash } = user;

Issue:

An ESLint error arises stating that 'hash' is assigned a value but never used (@typescript-eslint/no-unused-vars).

What would be an effective solution to address this problem without disregarding the ESLint rule?

Answer №1

It is important to properly define an interface and adhere to its rules when using it.

If you want to make the hash property optional from the start, you can modify the interface like this:

interface UserRecord {
  name: string;
  age: number;
  id: string;
  hash?: string
}

delete user.hash // no error

Alternatively, you can use the method of spreading objects and ignoring certain properties.

If you need to address eslint warnings, you can create a function that explicitly removes the hash property or utilize tools like `omit` from lodash/ramda:

function withoutHash (user: UserRecord): Omit<UserRecord, 'hash'> {
    return {
        name: user.name,
        id: user.id,
        age: user.age
    }
}

Check out the typescript playground for more details.

Answer №2

You can utilize classes to achieve this functionality as they act as types. Here is a more advanced solution:

class Person {
  name = ''
}

class PersonWithId extends Person {
  id = ''
  // add any other properties you need
}

Next, you can create a function to clean objects like so:

function clearObject<T>(C: any, o: any, f: any = {} ): T {
  Object.keys( C ).forEach( ( key: any ) => {

      const keyType = typeof C[ key ]
      const Value = o[ key ] ?? C[ key ] 

      if (
        !!Value &&
        keyType === 'object' &&
        Object.keys( Value ).length > 0
      ) {
        // if the key is another object
        if ( !Array.isArray( Value ) ) {
          f[ key ] = clearObject( C[ key ], Value )
          return f
        }

        // if the key is an array
        Value.forEach( ( items: any ) => {
          f[ key ] = clearObject( C[ key ], items, f[ key ] )
          return f
        } )
      }
      
      // assign the value to its key
      f[ key ] = Value
    } )
    // return the new object cleaned and typed
    return f as T
}

This function will iterate through each key of the type class and extract only keys that exist in your type class. It then creates a new object and returns it with the correct type. You can use it like this:

const person: PersonWithId = {
  name: 'John',
  id: 1
}

const cleared = clearObject<Person>(new Person(), person)

Now, note that the cleared variable is of type Person, and if you attempt to access the ID property from it, TypeScript will show an error.

This method works for clearing any object based on another type class without encountering any TypeScript errors.

I have provided a working example on CodeSandbox. Alternatively, if you do not require nested object key validation, you can opt for this simplified solution:

// define classes as types
class PublicPerson {
  name = "";
  age = 99;
}
class PrivatePerson extends PublicPerson {
  id = "";
  hash = "";
}

// object cleaning function
function clearObject<T>(newType: T, current: any): T {
  const cleared: any = {};
  Object.keys(newType).forEach((key) => {
    // assign the value to its key
    // if the key does not exist in the current object
    // we get the value from newType
    // e.g., if the current object lacks the 'age' property,
    // the assigned value will be '99'
    // as it's the default value for the class
    cleared[key] = current[key] ?? newType[key];
  });
  // return the new object cleaned and typed
  return cleared as T;
}

// your object
const person: PrivatePerson = {
  name: "Person Name",
  age: 18,

  id: "123",
  hash: "aPrivatePasswordHash"
};

// cleaned object
// this is useful on the backend if you wish to
// send a person's data to a client
// you'll always send cleaned public data
const cleaned = clearObject<PublicPerson>(new PublicPerson(), person);

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

Determine the generic types of callback in TypeScript based on the argument provided

There are numerous Stack Overflow questions that share a similar title, but it seems none of them address this particular inquiry. I'm in the process of developing a wrapper for an express RequestHandler that can catch errors in asynchronous handlers ...

Angular Universal involves making two HTTP calls

When using Angular Universal, I noticed that Http calls are being made twice on the initial load. I attempted to use transferState and implemented a caching mechanism in my project, but unfortunately, it did not resolve the issue. if (isPlatf ...

Tips for creating a seamless merge from background color to a pristine white hue

Seeking a seamless transition from the background color to white at the top and bottom of the box, similar to the example screenshot. Current look: The top and bottom of the box are filled with the background color until the edge https://i.stack.imgur.com ...

An unusual error occurred stating that the `forEach` property does not exist on the given type

I am working on a chess game and encountering some Typescript errors that I'm struggling to comprehend. The issue arises in the following class method: clickEvent (e: MouseEvent): void { const coordinates: ClientRect = this.chessBoard.getBounding ...

Exploring the process of introducing a new property to an existing type using d.ts in Typescript

Within my src/router.ts file, I have the following code: export function resetRouter() { router.matcher = createRouter().matcher // Property 'matcher' does not exist on type 'VueRouter'. Did you mean 'match'? } In an ...

Utilizing JavaScript variables imported from an external library in Next.js: A Guide

I am currently working on a Next.js with Typescript website and I am in the process of adding advertisements. The ad provider has given me instructions to embed this JavaScript code on my site: <script src="//m.servedby-buysellads.com/monetization. ...

Error: Namespace declaration does not have a type annotation - TypeScript/Babel

After creating my app using the command npx create-react-app --typescript, I encountered an issue with generated code and namespaces causing Babel to throw an error. Here are the steps I took to try and resolve the issue: I ejected the project Updated b ...

Issues with showing data in Angular Material tables

I recently deployed my Angular Application on a server and encountered an issue with the Angular Material table. Despite data being present in the logs, it does not display on the table. This problem only occurs in production, as everything works perfectl ...

The Angular Progressive Web App functions properly in ng serve mode, but encounters issues when running with http-server

I'm developing a Progressive Web App (PWA) using Angular. Everything was functioning smoothly until out of nowhere, I started encountering a 404 Error whenever I tried to navigate to a new component while serving in dist/project with http-server. Surp ...

Getting the parent from a child in Typescript: Best Practices

Querying: In TypeScript, is it possible to retrieve a parent instance from a child instance? I am aware that casting a child into its parent is a method, however, the child's additional properties still exist in the parent, albeit concealed. Check o ...

Typescript: When using ts-node-dev, an error occurred while trying to import express due to an unexpected

I am embarking on a fresh project using Typescript and I intend to set up the node server with typescript utilizing express. There's a helpful tutorial that explains how to execute a Typescript file without going through the hassle of compiling files, ...

Invoking vscode Extension to retrieve data from webview

One task I'm currently working on involves returning a list from the extension to be displayed in the input box of my webview page. The idea is for a JavaScript event within the webview to trigger the extension, receive the list object, and then rend ...

Transferring AgGrid context in a functional React component

I have been working on a component that utilizes AgGrid to display a table, with the data sourced from a Redux selector. My goal is to include a button within a cell in the table that triggers an action based on the specific row's data. However, I a ...

Steps for eliminating a selection in the dropdown list:

I am dealing with a situation in which my select element gets new options added based on a specific input value. However, each time the value is changed, more options are appended to the select element instead of replacing the old ones. How can I remove th ...

The 'BaseResponse<IavailableParameters[]>' type does not contain the properties 'length', 'pop', etc, which are expected to be present in the 'IavailableParameters[]' type

After making a get call to my API and receiving a list of objects, I save that data to a property in my DataService for use across components. Here is the code snippet from my component that calls the service: getAvailableParameters() { this.verifi ...

Creating Child Components in Vue Using Typescript

After using Vue for some time, I decided to transition to implementing Typescript. However, I've encountered an issue where accessing the child's methods through the parent's refs is causing problems. Parent Code: <template> <re ...

Issue: NullInjectorError: R3InjectorError(AuthorModule)[ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelper]:

While I am going through a tutorial on the abp framework, I encountered an error with the Author route that says "ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(AuthorModule)[ScrollbarHelper -> ScrollbarHelper -> ScrollbarHelp ...

The new data is not being fetched before *ngFor is updating

In the process of developing a "Meeting List" feature that allows users to create new meetings and join existing ones. My technology stack includes: FrontEnd: Angular API: Firebase Cloud Functions DB: Firebase realtime DB To display the list of meeting ...

How can I best declare a reactive variable without a value in Vue 3 using TypeScript?

Is there a way to initialize a reactive variable without assigning it a value initially? After trying various methods, I found that using null as the initial value doesn't seem to work: const workspaceReact = reactive(null) // incorrect! Cannot pass n ...

Troubleshoot: Issue with binding data from DynamicComponentLoader in Angular 2 template

My implementation involves the utilization of DynamicComponentLoader and is based on the Angular2 API Guide. https://angular.io/docs/ts/latest/api/core/DynamicComponentLoader-class.html The code structure I have set up looks like this: import {Page} fro ...