oxc-project/oxc · warning · OxcDiagnostic
Do not delete dynamically computed property keys.
Error message
Do not delete dynamically computed property keys.
What it means
typescript/no-dynamic-delete reports `delete obj[computedKey]` — the delete operator applied to a computed member expression. The rule's note cites V8 behavior: frequent deletions can push objects into dictionary-mode (slow) properties and hurt inline caches. Help text suggests a static key or a Map/Set for dynamic keys.
Source
Thrown at crates/oxc_linter/src/rules/typescript/no_dynamic_delete.rs:44
/// ```ts
/// const container: { [i: string]: 0 } = {};
/// delete container['aa' + 'b'];
/// ```
///
/// Examples of **correct** code for this rule:
/// ```ts
/// const container: { [i: string]: 0 } = {};
/// delete container.aab;
/// ```
NoDynamicDelete,
typescript,
restriction,
version = "0.5.2",
short_description = "Disallow using the delete operator on computed key expressions.",
);
fn no_dynamic_delete_diagnostic(span: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("Do not delete dynamically computed property keys.")
.with_help("Use a static property key, or use a Map/Set for dynamic keys.")
.with_note(
"Frequent property deletions can move objects to slower dictionary-mode properties and hurt inline-cache optimizations. See: https://v8.dev/blog/fast-properties.",
)
.with_label(span)
}
impl Rule for NoDynamicDelete {
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
let AstKind::UnaryExpression(expr) = node.kind() else { return };
if !matches!(expr.operator, UnaryOperator::Delete) {
return;
}
let Expression::ComputedMemberExpression(computed_expr) = &expr.argument else { return };
let inner_expression = computed_expr.expression.get_inner_expression();
if inner_expression.is_string_literal() || inner_expression.is_number_literal() {
return;View on GitHub (pinned to e1e7af627c)
Solutions
- Use a Map and `cache.delete(key)` — designed for dynamic keys
- Use a Set for presence tracking: `set.delete(key)`
- If the key set is known, switch to static deletion: `delete obj.fixedKey`
- If object shape must be preserved, set to undefined instead of deleting: `obj[key] = undefined` (note: shape stays fast but the key remains)
Example fix
// before
const cache: Record<string, Data> = {};
delete cache[key];
// after
const cache = new Map<string, Data>();
cache.delete(key); Defensive patterns
Strategy: fallback
Validate before calling
// .oxlintrc.json
{
"rules": { "typescript/no-dynamic-delete": "warn" }
}
// quick audit for the pattern before adopting the rule:
// rg --type ts "delete\s+\w+\[" src/ Prevention
- Model dynamic-key stores with Map/Set from the start; their delete() is designed for it
- Avoid delete on hot-path objects in performance-sensitive code (V8 dictionary-mode transition)
- When shape stability matters more than removal, assign undefined instead of deleting
When it happens
Trigger: Any UnaryExpression with operator `delete` whose argument is a computed member access: `delete cache[key]`, `delete obj[propName]`, `delete map[`${a}-${b}`]`; deleting a static property (`delete obj.key`) is not reported.
Common situations: Hand-rolled caches or registries using plain objects with variable keys; porting Map-like logic from objects; memoization tables that grow and shrink; config maps where entries are removed at runtime.
Related errors
- encountered allocation error
- Do not use @ts-{ts_comment_name} because it alters compilati
- Use "@ts-expect-error" instead of @ts-ignore, as "@ts-ignore
- Type can be trivially inferred from the initializer
- ES2015 module syntax is preferred over namespaces.
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/0de5e976163cd048.
Report an issue: GitHub.