{"record":{"id":"dd858fa4c408067b","repo":"oxc-project/oxc","slug":"use-structuredclone-to-create-a-deep-clone","errorCode":null,"errorMessage":"Use `structuredClone(…)` to create a deep clone.","messagePattern":"Use `structuredClone\\(…\\)` to create a deep clone\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/unicorn/prefer_structured_clone.rs","lineNumber":22,"sourceCode":"    AstKind,\n    ast::{CallExpression, Expression},\n};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_span::Span;\nuse schemars::JsonSchema;\nuse serde::Deserialize;\n\nuse crate::{\n    AstNode,\n    ast_util::is_method_call,\n    context::LintContext,\n    fixer::{RuleFix, RuleFixer},\n    rule::{DefaultRuleConfig, Rule},\n};\n\nfn prefer_structured_clone_diagnostic(span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(\"Use `structuredClone(…)` to create a deep clone.\")\n        .with_help(\"Switch to `structuredClone(…)`.\")\n        .with_label(span)\n}\n\n#[derive(Debug, Default, Clone, Deserialize)]\npub struct PreferStructuredClone(Box<PreferStructuredCloneConfig>);\n\n#[derive(Debug, Clone, JsonSchema, Deserialize)]\n#[serde(rename_all = \"camelCase\", default, deny_unknown_fields)]\npub struct PreferStructuredCloneConfig {\n    /// List of functions that are allowed to be used for deep cloning instead of structuredClone.\n    functions: Vec<String>,\n}\n\nimpl Default for PreferStructuredCloneConfig {\n    fn default() -> Self {\n        Self { functions: vec![\"cloneDeep\".to_string(), \"utils.clone\".to_string()] }\n    }","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/unicorn/prefer_structured_clone.rs#L4-L40","documentation":"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.","triggerScenarios":"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)`.","commonSituations":"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.","solutions":["Replace `JSON.parse(JSON.stringify(obj))` with `structuredClone(obj)`.","For utility calls, switch to `structuredClone` or add your preferred helper to the allow-list: `{ \"rules\": { \"unicorn/prefer-structured-clone\": [\"error\", { \"functions\": [\"cloneDeep\"] }] } }`.","Verify your runtime supports it (Node 17+, Deno, browsers since ~2022); for older targets keep the old code and disable the rule.","Remember `structuredClone` cannot clone functions, DOM nodes, or property descriptors/prototypes — if you rely on those, keep your custom clone and allow-list it."],"exampleFix":"// before\nconst draft = JSON.parse(JSON.stringify(state));\n\n// after\nconst draft = structuredClone(state);","handlingStrategy":"validation","validationCode":"// Prefer the platform API for deep copies\nconst draft = structuredClone(state);\n// If you must keep a helper, allow-list it:\n// .oxlintrc.json: \"unicorn/prefer-structured-clone\": [\"error\", { \"functions\": [\"cloneDeep\"] }]","typeGuard":null,"tryCatchPattern":"// structuredClone throws DataCloneError on non-cloneable values — catch it at the boundary\ntry {\n  const copy = structuredClone(value);\n} catch (err) {\n  if (err instanceof Error && err.name === 'DataCloneError') {\n    // handle functions/DOM nodes/prototype-carrying objects explicitly\n  } else throw err;\n}","preventionTips":["Never deep-clone via JSON round-trip: it drops undefined, mangles Date/Map/Set, and throws on cycles.","Confirm Node 17+/modern browser support before standardizing on structuredClone.","Allow-list lodash `cloneDeep` via the `functions` config when prototypes or functions must survive the copy."],"tags":["oxlint","unicorn","deep-clone","structured-clone","json-pitfalls"],"backgroundTag":"structured-clone-migration","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}