Best practice for object cloning (in TS)?

Looking to get deep copies of things like EmbedBuilder. structuredClone doesn't copy functions. Should we be doing something like const secondEmbed = new EmbedBuilder({...firstEmbed})?
16 Replies
d.js toolkit
d.js toolkit2w ago
BotDev
BotDevOP2w ago
node ver is 22.12.0 djs ver is 14.24.2 typescript ver is 5.9.3
Mafia
Mafia2w ago
is firstEmbed an EmbedBuilder?
BotDev
BotDevOP2w ago
Yeah, a previously defined EmbedBuilder with fields, description, contents, or whatever else previously set. I want to be able to make a clone and modify it without side effects polluting the prior embed.
Mafia
Mafia2w ago
then just set
const secondEmbed = firstEmbed
const secondEmbed = firstEmbed
BotDev
BotDevOP2w ago
Will assignment provide a deep copy by value by default? So if I e.g. tamper with secondEmbed's fields after that, they won't change firstEmbed?
Mafia
Mafia2w ago
that is my understanding
BotDev
BotDevOP2w ago
Thank you. Will test it out RQ and see. So I tried two things: First:
function editField(embed: EmbedBuilder, field:APIEmbedField): EmbedBuilder {
const newEmbed = embed;
let adjustedFields = [... embed.data.fields!]

const fieldIndex = adjustedFields.findIndex(f => f.name === field.name)
adjustedFields[fieldIndex] = field;
newEmbed.setFields(adjustedFields);

return newEmbed;
}
function editField(embed: EmbedBuilder, field:APIEmbedField): EmbedBuilder {
const newEmbed = embed;
let adjustedFields = [... embed.data.fields!]

const fieldIndex = adjustedFields.findIndex(f => f.name === field.name)
adjustedFields[fieldIndex] = field;
newEmbed.setFields(adjustedFields);

return newEmbed;
}
This did not work. It tainted the prior EmbedBuilder object. Second, I tried the same thing, but replacing
const newEmbed = embed;
const newEmbed = embed;
with
const newEmbed = new EmbedBuilder(embed.data);
const newEmbed = new EmbedBuilder(embed.data);
, and that worked as expected. However, I am looking for a more general, idiomatic way to accomplish this more uniformly across various objects in the discord.js library.
d.js docs
d.js docs2w ago
:method: (static) EmbedBuilder#from() discord.js@14.24.2 Creates a new embed builder from JSON data
Mafia
Mafia2w ago
this would be another was to go from APIdata to builder
BotDev
BotDevOP2w ago
Cool, the #from method looks like it might be used across some other classes like ActionRowBuilder, so that might be the idiom some of the devs want us to use. Just tested
const newEmbed = EmbedBuilder.from(embed);
const newEmbed = EmbedBuilder.from(embed);
and it works the same way as cloning from data. Good stuff. Thank you.
Mafia
Mafia2w ago
glad #from worked out went an looked up the source code it is pretty simple
return new this(isJSONEncodable(other) ? other.toJSON() : other);
return new this(isJSONEncodable(other) ? other.toJSON() : other);
BotDev
BotDevOP2w ago
In VS Code when I hit f12 on EmbedBuilder to hop to implementation, I see this:
export class EmbedBuilder extends BuildersEmbed {
public constructor(data?: EmbedData | APIEmbed);
public override setColor(color: ColorResolvable | null): this;
public static from(other: JSONEncodable<APIEmbed> | APIEmbed): EmbedBuilder;
export class EmbedBuilder extends BuildersEmbed {
public constructor(data?: EmbedData | APIEmbed);
public override setColor(color: ColorResolvable | null): this;
public static from(other: JSONEncodable<APIEmbed> | APIEmbed): EmbedBuilder;
How'd you wind up landing on that piece of the source? Trying to figure out if there's a convenient way for me to jump there. Short of like going into node_modules and navigating the tree by hand.
BotDev
BotDevOP2w ago
discord.js
discord.js
discord.js is a powerful Node.js module that allows you to interact with the Discord API very easily. It takes a much more object-oriented approach than most other JS Discord libraries, making your bot's code significantly tidier and easier to comprehend.
Mafia
Mafia2w ago
ya got it from the docs code link
BotDev
BotDevOP2w ago
Good to know. Thank you!

Did you find this page helpful?