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

Unable to locate the next/google/font module in my Typescript project

Issue With Import Syntax for Font Types The documentation here provides an example: import { <font-name> } from 'next/google/font'; This code compiles successfully, but throws a "module not found" error at runtime. However, in this disc ...

Combining platform-express and platform-fastify for optimal performance

I am currently working on a NestJS application and my goal is to upload files using the package @types/multer. However, I encountered an issue while following the guidelines from the official documentation: Upon starting my application development, I dec ...

The import of type cannot be done within paths in tsconfig

Currently, I am tackling a server side project utilizing TypeScript. In this context, I have established various types in ooo.d.ts and configured the paths within tsconfig.json. However, upon attempting to import the specified type, an error is being displ ...

TypeScript's type inference feature functions well in scenario one but encounters an error in a different situation

I recently tried out TypeScript's type inference feature, where we don't specify variable types like number, string, or boolean and let TypeScript figure it out during initialization or assignment. However, I encountered some confusion in its be ...

elimination of nonexistent object

How can I prevent releasing data if two attributes are empty? const fork = [ { from: 'client', msg: null, for: null }, { from: 'client', msg: '2222222222222', for: null }, { from: 'server', msg: 'wqqqqqqqq ...

Error TS2339: The member 'sort' is not found in the type 'BehaviorSubject<AbstractControl[]>'

Looking to create a dynamic table using Angular material where I can dynamically add or remove data. As a newcomer to Angular, I found the following StackBlitz helpful in setting up the table: https://stackblitz.com/edit/angular-material-editable-table-fa ...

A guide on creating a concatenation function using TypeScript

Looking for a way in Typescript to define an interface Calculator that allows for concatenation capabilities? interface Calculator { ... } let calcu: Calculator; calcu(2).multiply(5).add(1) I attempted the following: interface Calculator { (num: n ...

A comprehensive guide on utilizing the ngFor directive for looping through objects

After trying to iterate over this dataset within my HTML, I attempted a nested ngfor, but unfortunately encountered an error. My attempt involved iterating the object twice with a nested ngfor, resulting in the following error: HabitRecordsComponent.ht ...

Cypress - Adjusting preset does not impact viewportHeight or Width measurements

Today is my first day using cypress and I encountered a scenario where I need to test the display of a simple element on mobile, tablet, or desktop. I tried changing the viewport with a method that seems to work, but unfortunately, the config doesn't ...

Typescript and React: Unraveling the intricacies of complex

Is it possible to define custom types verified by a function in Typescript/React? Instead of using a simple 'string' type, I envision using a regex expression: interface Verify { email: /.+@.*\.com/g; } The specific regex above might not ...

Creating a personalized HTML email template with a TypeScript foreach loop

As I work on developing an app, users will have the opportunity to share product information via email. Each product includes a title, image, description, ingredients, and a list of energy values. Currently, I am able to send an email with all the product ...

Control the transparency of the initial parent div that includes an *ngFor loop

I want to add opacity only to the first div which contains an icon and a heading in another nested div. The second div should remain fully visible (opacity: 1). Here is the HTML structure: <div class="row clearfix"> <div class="col-lg-3 col- ...

The type 'Dispatch<SetStateAction<boolean>>' cannot be assigned to type 'boolean'

Currently, I am attempting to transfer a boolean value received from an onChange function to a state variable. let [toggleCheck, setToggleCheck] =useState(false);` <input type="checkbox" id={"layout_toggle"} defaultChecked={toggleCh ...

Exploring the Power of TextEncoding in TS 2.8 within the Angular 6 Environment

I'm facing a challenging issue while trying to import TextEncoding in TS 2.8. I have installed it using npm and attempted to import it like this: import { TextDecoder } from 'text-encoding'; Alternatively, import { } from 'text-encod ...

Struggling to make HttpClient Post work in Angular 5?

I'm facing an issue with my httpClient post request. The service is not throwing any errors, but it's also not successfully posting the data to the database. Below is the code snippet: dataService.ts import { Injectable } from '@angular/c ...

What is the best way to set a Firestore data field as a variable?

I am working with a firebase collection named 'messages', where I add documents as follows: this.afs.collection('messages').add({ 'qn': message, 'student': this.student, 'upvotes': this.upvote }); The upv ...

Obtain accurate dispatch type by leveraging ConnectedProps in conjunction with redux-thunk

Currently, I am utilizing Redux-Toolkit and Typescript. Specifically, my goal is to implement ConnectedProps as recommended in the redux documentation. However, it appears that the dispatch type is not recognized correctly (it is identified as a normal Dis ...

The object literal is limited to defining recognized properties, and 'clientId' is not present in the 'RatesWhereUniqueInput' type

Currently, I am using typescript alongside prisma and typegraphql in my project. However, I have encountered a type error while working with RatesWhereUniqueInput generated by prisma. This input is classified as a "CompoundUniqueInput" due to the database ...

The input tag loses focus after its value is updated using a class method in Angular 8.x

Currently, I am working on integrating a credit card payment method and formatting its number through specific methods. Here is how it is done: HTML <div class="form-group" *ngFor="let formField of cardFields; let cardFieldIndex = index;"> ...

What is preventing the getters for form errors from functioning in my Angular Reactive Form component template, while a direct reference does work?

I have a question regarding a small issue with an Angular reactive form in a lazily loaded signup module. The code structure is structured as follows: TS get email() { return this.myForm.get('email'); } // While this code works from within th ...