Last updated: Apr 5, 2024
Reading timeยท6 min
NOTE: if you got the error when using JEST and JSDOM, click on the following subheading:
The "ReferenceError: structuredClone is not defined" error occurs when you use
the structuredClone()
method in an older Node.js environment.
To solve the error, make sure you're running Node.js version 17 or greater.
As shown in
this table,
the structuredClone
method was introduced in Node.js version 17.0.0.
You can use the node -v
command to get your version of Node.js.
node -v
If you try to run the following code sample with Node < 17, the error occurs.
const obj = { first: 'bobby', last: 'hadz', address: { country: 'Country 123', city: 'City 123', }, }; // โ๏ธ ReferenceError: structuredClone is not defined console.log(structuredClone(obj));
The structuredClone method is used to create a deep clone of the supplied object.
There are 3 common ways to solve the error:
JSON.stringify()
and JSON.parse()
methods to create a deep clone
of the object.lodash.cloneDeep
method.NOTE: if you got the error when using JEST and JSDOM, click on the following subheading:
There is a hacky way to create a deep clone of an object using the JSON.stringify and JSON.parse methods.
const obj = { first: 'bobby', last: 'hadz', address: { country: 'Country 123', city: 'City 123', }, }; const deepCopy = JSON.parse(JSON.stringify(obj)); // { // first: 'bobby', // last: 'hadz', // address: { country: 'Country 123', city: 'City 123' } // } console.log(deepCopy);
JSON.stringify()
method is used to convert the object to a JSON string.JSON.parse()
method is used to convert the JSON string to an object.This works because when the object gets converted to a string, the references to the nested objects are lost.
const obj = { first: 'bobby', last: 'hadz', address: { country: 'Country 123', city: 'City 123', }, }; const deepCopy = JSON.parse(JSON.stringify(obj)); deepCopy.address.city = 'New city'; console.log(deepCopy.address.city); // ๐๏ธ New City // โ The original remains unchanged console.log(obj.address.city); // ๐๏ธ City 123
As shown in the code sample, modifying a nested property in the copied object doesn't modify the property in the original object.
There are some issues with using the JSON.parse()
and JSON.stringify()
methods - mainly around objects that recursively reference themselves.
An alternative approach is to use the lodash.clonedeep method.
You can install the module by running the following commands.
# ๐๏ธ with NPM npm install lodash.clonedeep # ๐๏ธ Only if you use TypeScript npm install --save-dev @types/lodash.clonedeep # ------------------------------------------ # ๐๏ธ with YARN yarn add lodash.clonedeep # ๐๏ธ Only if you use TypeScript yarn add @types/lodash.clonedeep --dev
After you install the module, import and use the cloneDeep
method.
import cloneDeep from 'lodash.clonedeep'; // ๐๏ธ Uncomment this if you use require() // const cloneDeep = require('lodash.clonedeep'); const obj = { first: 'bobby', last: 'hadz', address: { country: 'Country 123', city: 'City 123', }, }; const deepCopy = cloneDeep(obj); // { // first: 'bobby', // last: 'hadz', // address: { country: 'Country 123', city: 'City 123' } // } console.log(deepCopy);
If you use CommonJS, use the require()
import statement instead.
The cloneDeep
method recursively clones the supplied value and returns the
result.
Changing a nested property in the cloned object doesn't change the property in the original object.
import cloneDeep from 'lodash.clonedeep'; const obj = { first: 'bobby', last: 'hadz', address: { country: 'Country 123', city: 'City 123', }, }; const deepCopy = cloneDeep(obj); deepCopy.address.city = 'New city'; console.log(deepCopy.address.city); // ๐๏ธ New City // // โ The original remains unchanged console.log(obj.address.city); // ๐๏ธ City 123
We changed the value of the address.city
property in the cloned object but the
original object remains the same.
If you use TypeScript, make sure you have a recent version of the @types/node
package installed.
# with NPM npm install @types/node@latest --save-dev # with YARN yarn add @types/node@latest --dev
You can also update your version of typescript
if the installed module is out
of date.
# with NPM npm install typescript@latest --save-dev # with YARN yarn add typescript@latest --dev
As previously noted, the structuredClone
method was introduced in Node.js
version 17.0.0.
If the output of the node -v
command is less than 17
, then you have to
install the long-term supported version of Node.js.
One way to install the LTS Node.js version is to use the official Node.js website.
At the time of writing the LTS Node.js version is 18.15.0 where the
structuredClone
method is available.
Here are other examples of packages that enable you to manage your Node.js version:
For example, if you install the nvm for macOS and Linux package, you can use the following commands to switch to the LTS version.
# NVM for macOS and Linux nvm install --lts nvm use --lts
If you install the nvm for Windows package, use the following commands instead.
# NVM for Windows nvm install lts nvm use lts
If you install the n package, use the following commands.
# using n package npm install -g n n lts
If you install the volta package, use the following commands.
# using the Volta package volta install node
If you use Chocolatey
on Windows:
Click on the Search bar and type CMD.
Right-click on the Command Prompt application and click "Run as administrator".
choco install nodejs --version=18.5.0
You can issue the node -v
command to verify your version of Node.js is greater
than 17.
You can use the structuredClone
method if your Node.js version is 17 or
greater.
const obj = { first: 'bobby', last: 'hadz', address: { country: 'Country 123', city: 'City 123', }, }; console.log(structuredClone(obj));
If you got the "structuredClone is not defined" error when working in jest
and
jsdom
, use a polyfill.
Open your terminal and run the following command to install the @ungap/structured-clone package.
npm install @ungap/structured-clone # ๐๏ธ only if you use TypeScript npm install --save-dev @types/ungap__structured-clone
Open your jest.config.js
or jest.config.ts
file and set the
structuredClone
global.
import structuredClone from '@ungap/structured-clone' export default { // ... rest of your config globals: { structuredClone, }, };
Alternatively, you can import the structuredClone
polyfill in the files where
the method is used.
An alternative approach to solving the error is to extend from the
JSDOMEnvironment
class and set the structuredClone
global.
In the root directory of your project, right next to jest.config.js
, create a
file called JSDOMEnvironmentPatch.js
and add the following code to it.
import JSDOMEnvironment from 'jest-environment-jsdom'; export default class JSDOMEnvironmentPatch extends JSDOMEnvironment { constructor(...args) { super(...args); this.global.structuredClone = structuredClone; } }
Update the testEnvironment
property in your jest.config.js
class.
module.exports = { testEnvironment: './JSDOMEnvironmentPatch.js', // ... rest of your config };
Here is the TypeScript equivalent of the file.
import JSDOMEnvironment from 'jest-environment-jsdom'; export default class JSDOMEnvironmentPatch extends JSDOMEnvironment { constructor( ...args: ConstructorParameters<typeof JSDOMEnvironment> ) { super(...args); this.global.structuredClone = structuredClone; } }
Make sure to correct the extension when setting thetestEnvironment
property if
you use TypeScript.
module.exports = { testEnvironment: './JSDOMEnvironmentPatch.ts', // ... rest of your config };
Make sure you have the jest-environment-jsdom module installed.
# with NPM npm install --save-dev jest-environment-jsdom # with YARN yarn add jest-environment-jsdom --dev
After you set the structuredClone
global to the actual method, the error
should be resolved.
An alternative approach is to monkey patch the structuredClone
global.
For example, you can create a file called structuredClonePatch.js
with the
following contents.
if (!global.structuredClone) { global.structuredClone = function structuredClone(obj) { return JSON.parse(JSON.stringify(obj)); }; }
And import the file in the entry point of your application (e.g. index.js
or
index.ts
).
import './structuredClonePatch.js'
The file has to be imported before any references to the structuredClone
global method.
We simply check if the method doesn't exist on the global object.
If the condition is met, we set the global to the combination of JSON.parse()
and JSON.stringify()
instead.
Alternatively, you can mock the structuredClone
method in your test file.
global.structuredClone = jest.fn(val => { return JSON.parse(JSON.stringify(val)); });
You should generally stick to the polyfill solution or extend the
JSDOMEnvironment
environment class, however, if nothing else works, monkey
patching or mocking the method gets the job done.