TS2790: The operand of a 'delete' operator must be optional
ID: typescript/ts2790-delete-must-be-optional
Version Compatibility
| Version | Status | Introduced | Deprecated | Notes |
|---|---|---|---|---|
| 5 | active | — | — | — |
Root Cause
TypeScript 4.0+ with strict mode (exactOptionalPropertyTypes or strict) enforces that the delete operator can only be used on properties marked as optional (?:). Deleting a required property would leave the object in a state that violates its type contract -- the property is declared as always present but is now undefined. This error catches potential runtime bugs where code assumes a property exists but it has been deleted.
genericWorkarounds
-
95% success Mark the property as optional in the type definition
// Before: required property interface Config { apiKey: string; debug: boolean; } const config: Config = { apiKey: 'abc', debug: true }; delete config.debug; // TS2790 // After: mark as optional interface Config { apiKey: string; debug?: boolean; // Now optional } const config: Config = { apiKey: 'abc', debug: true }; delete config.debug; // OKSources: https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties
-
92% success Use destructuring with rest to create a new object without the property instead of mutating
// Instead of deleting, create a new object without the property: interface FullUser { id: string; name: string; password: string; } type SafeUser = Omit<FullUser, 'password'>; const user: FullUser = { id: '1', name: 'Alice', password: 'secret' }; // Don't do: delete user.password; // Do this instead: const { password, ...safeUser }: FullUser = user; // safeUser is { id: string; name: string } - type-safe, no mutation const result: SafeUser = safeUser;Sources: https://www.typescriptlang.org/docs/handbook/variable-declarations.html#object-destructuring
-
85% success Use Partial<T> for objects where properties may be removed at runtime
// When the object is expected to have properties removed over its lifetime: interface FormFields { name: string; email: string; phone: string; } // Use Partial when the object will be mutated by deleting fields const draft: Partial<FormFields> = { name: 'Bob', email: '[email protected]', phone: '555' }; delete draft.phone; // OK - all properties in Partial are optional // When you need the full object later, validate: function isComplete(f: Partial<FormFields>): f is FormFields { return !!f.name && !!f.email && !!f.phone; }Sources: https://www.typescriptlang.org/docs/handbook/utility-types.html#partialtype
Dead Ends
Common approaches that don't work:
-
Use @ts-ignore or @ts-expect-error to suppress the delete error
75% fail
Suppressing the error allows deleting a required property, making the object's runtime shape inconsistent with its type. Any subsequent code accessing the deleted property gets undefined instead of the declared type, causing runtime TypeError ('Cannot read property of undefined'). The type system can no longer protect you.
-
Cast the object to 'any' before deleting the property
80% fail
Casting to any bypasses the type check but the underlying problem remains: the object's type declares the property as required. After casting back or using the object elsewhere with its original type, TypeScript assumes the property exists. This leads to the same runtime undefined access bugs that TS2790 is designed to prevent.