oxc-project/oxc · warning · OxcDiagnostic

Use `structuredClone(…)` to create a deep clone.

Error message

Use `structuredClone(…)` to create a deep clone.

What it means

This is the oxlint rule `unicorn/prefer-structured-clone`. It flags common hand-rolled deep clones — most notably `JSON.parse(JSON.stringify(obj))` — and known clone utility calls, recommending the platform's `structuredClone(…)`, which handles cyclic references, Maps, Sets, Dates, and typed arrays that the JSON round-trip silently corrupts. The `functions` config lists clone functions you are allowed to keep using.

Source

Thrown at crates/oxc_linter/src/rules/unicorn/prefer_structured_clone.rs:22

    AstKind,
    ast::{CallExpression, Expression},
};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{
    AstNode,
    ast_util::is_method_call,
    context::LintContext,
    fixer::{RuleFix, RuleFixer},
    rule::{DefaultRuleConfig, Rule},
};

fn prefer_structured_clone_diagnostic(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Use `structuredClone(…)` to create a deep clone.")
        .with_help("Switch to `structuredClone(…)`.")
        .with_label(span)
}

#[derive(Debug, Default, Clone, Deserialize)]
pub struct PreferStructuredClone(Box<PreferStructuredCloneConfig>);

#[derive(Debug, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct PreferStructuredCloneConfig {
    /// List of functions that are allowed to be used for deep cloning instead of structuredClone.
    functions: Vec<String>,
}

impl Default for PreferStructuredCloneConfig {
    fn default() -> Self {
        Self { functions: vec!["cloneDeep".to_string(), "utils.clone".to_string()] }
    }

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Replace `JSON.parse(JSON.stringify(obj))` with `structuredClone(obj)`.
  2. For utility calls, switch to `structuredClone` or add your preferred helper to the allow-list: `{ "rules": { "unicorn/prefer-structured-clone": ["error", { "functions": ["cloneDeep"] }] } }`.
  3. Verify your runtime supports it (Node 17+, Deno, browsers since ~2022); for older targets keep the old code and disable the rule.
  4. Remember `structuredClone` cannot clone functions, DOM nodes, or property descriptors/prototypes — if you rely on those, keep your custom clone and allow-list it.

Example fix

// before
const draft = JSON.parse(JSON.stringify(state));

// after
const draft = structuredClone(state);
Defensive patterns

Strategy: validation

Validate before calling

// Prefer the platform API for deep copies
const draft = structuredClone(state);
// If you must keep a helper, allow-list it:
// .oxlintrc.json: "unicorn/prefer-structured-clone": ["error", { "functions": ["cloneDeep"] }]

Try / catch

// structuredClone throws DataCloneError on non-cloneable values — catch it at the boundary
try {
  const copy = structuredClone(value);
} catch (err) {
  if (err instanceof Error && err.name === 'DataCloneError') {
    // handle functions/DOM nodes/prototype-carrying objects explicitly
  } else throw err;
}

Prevention

When it happens

Trigger: A call recognized as a deep-clone idiom: `JSON.parse(JSON.stringify(x))` (matched via `is_method_call` on the JSON pair), or calls to configured clone helpers (e.g. lodash-style `cloneDeep(x)`) that are not in the rule's allow-list. Fixer support (`RuleFix`/`RuleFixer`) can rewrite simple cases to `structuredClone(x)`.

Common situations: State duplication in reducers and cache copies; the classic bug is the JSON round-trip dropping `undefined` values, turning `Date`s into strings, `NaN` into `null`, and throwing on cycles — exactly what `structuredClone` (Node >= 17, modern browsers) fixes. Teams with lodash may add `cloneDeep` to the `functions` allow-list and keep it.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/dd858fa4c408067b. Report an issue: GitHub.