14

I have tried to read how to delete a property from an object in here: How do I remove a property from a JavaScript object?

it should use delete, I try it like this

const eventData = {...myEvent}; // myEvent is an instance of my `Event` class
delete eventData.coordinate; // I have error in here

but I have error like this

enter image description here

The operand of a 'delete' operator must be optional.ts(2790)

and then I read this : Typescript does not infer about delete operator and spread operator?

it seems to remove that error is by changing my tsconfig.json file using

{
  "compilerOptions": {
    ...
     "strictNullChecks": false, 
    }
    ...
}

but if I implement this, I will no longer have null checking

so how to delete a property from an object in Typescript without the operand of a 'delete' operator must be optional error?

sarah
  • 3,819
  • 4
  • 38
  • 80

6 Answers6

23

Typescript warns you about breaking the contract (the object won't have the required property anymore). One of the possible ways would be omitting the property when you cloning the object:

const myEvent = {
    coordinate: 1,
    foo: 'foo',
    bar: true
};

const { coordinate, ...eventData } = myEvent;
// eventData is of type { foo: string; bar: boolean; }

Playground


Operands for delete must be optional. When using the delete operator in strictNullChecks, the operand must be any, unknown, never, or be optional (in that it contains undefined in the type). Otherwise, use of the delete operator is an error.

Docs

Aleksey L.
  • 35,047
  • 10
  • 74
  • 84
  • 3
    Destructuring to omit the property when cloning is genius, saved me lots of TypeScript hassle, thanks! – Liz Jul 27 '22 at 01:55
  • 1
    Following from this answer, if you don't use the const `coordinate` then typescript eslint will complain that `coordinate` is "assigned a value but is never used". How would you over come this lint error? – Fez Abbas Nov 03 '22 at 11:05
  • 3
    @FezAbbas you can disable this rule for this specific case using `ignoreRestSiblings ` https://eslint.org/docs/latest/rules/no-unused-vars#ignorerestsiblings. `"@typescript-eslint/no-unused-vars": ["error", { "ignoreRestSiblings": true }]` – Aleksey L. Nov 06 '22 at 08:07
3

Personally, I created a specific function taking advantage of generics, and it worked well!

const removeAttrFromObject = <O extends object, A extends keyof O>(
  object: O,
  attr: A
): Omit<O, A> => {
  const newObject = { ...object }

  if (attr in newObject) {
    delete newObject[attr]
  }

  return newObject
}

The two generics O and A are making all the magic, especially the A generic: extends keyof O says "the second parameter must be an attribute from the object".

Then inside the function, TypeScript knows for sure that attr is an attribute from the object. I double-check with the attr in newObject if statement, in order to make sure at runtime everything is fine too. Just pure defensive programming.

The const newObject = { ...object } is here to make sure we don't modify the reference passed as arg, but return a new reference instead. It's some leftovers from my functional programming old days.

Now if we try the function :

const test = removeAttrFromObject({ a: 1, b: 2 }, 'a')
// returns as type `Omit<{a: 1, b: 2}, 'a'>` which equals `{a: 1}`

const test2 = removeAttrFromObject({ a: 1, b: 2 }, 'c') 
// TypeScript raise an error as `'c'` is not a key from the object passed
Loïc Goyet
  • 720
  • 6
  • 6
2

You can delete if the property is optional

interface OptionalCoordinate {
   coordinate?
}

const eventData: OptionalCoordinate = {...myEvent}; // myEvent is an instance of my `Event` class
// ok to delete
delete eventData.coordinate; 
Prabhjot
  • 39
  • 1
  • 4
0

What the error is telling you is that coordinate is not an optional property of that object (that is it can be undefined) and you are trying to delete it.

You could have deleted it if it was optional

coordinate?: FirebaseFirestore.Geopoint

It seems to have been introduced after version 4.0, you can read more about it here.

jperl
  • 4,662
  • 2
  • 16
  • 29
0

Avoid the error by casting to any:

const user = {
    firstName: 'billy',
    middleName: 'the',
    lastName: 'kid'
};

delete (user as any).middleName

console.log(user)
// { firstName: 'billy', lastName: 'kid' }

Andrew Schreiber
  • 14,344
  • 6
  • 46
  • 53
-3

As many people has already pointed out, the following is a workable but not recommending way as it is not type-safe:

/* eslint-disable @typescript-eslint/no-explicit-any */
delete (eventData as any).coordinate;
Bill
  • 63
  • 5
  • This will shutdown any TypeScript warning, but does not makes the code safe. TypeScript works in a way that an object typed can have additional properties from the type defined to a value, that's why the errors pop on first hand. And your solution doesn't address it – Loïc Goyet Mar 06 '23 at 14:14