typescript type_error ai_generated true

TS2790: The operand of a 'delete' operator must be optional

ID: typescript/ts2790-delete-must-be-optional

Also available as: JSON · Markdown
93%Fix Rate
90%Confidence
40Evidence
2021-06-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
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.

generic

Workarounds

  1. 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; // OK

    Sources: https://www.typescriptlang.org/docs/handbook/2/objects.html#optional-properties

  2. 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

  3. 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:

  1. 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.

  2. 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.

Error Chain

Leads to:
Preceded by:
Frequently confused with: