typescript - cloning object
Solving The Specific Issue
You can use a type assertion to tell the compiler that you know better:
public clone(): any {
var cloneObj = new (this.constructor() as any);
for (var attribut in this) {
if (typeof this[attribut] === "object") {
cloneObj[attribut] = this[attribut].clone();
} else {
cloneObj[attribut] = this[attribut];
}
}
return cloneObj;
}
Cloning
As of 2022, there is a proposal to allow structuredClone
to deep copy many types.
const copy = structuredClone(value)
There are some limitations on what kind of thing you can use this on.
Bear in mind that sometimes it is better to write your own mapping - rather than being totally dynamic. However, there are a few "cloning" tricks you can use that give you different effects.
I will use the following code for all the subsequent examples:
class Example {
constructor(public type: string) {
}
}
class Customer {
constructor(public name: string, public example: Example) {
}
greet() {
return 'Hello ' + this.name;
}
}
var customer = new Customer('David', new Example('DavidType'));
Option 1: Spread
Properties: Yes
Methods: No
Deep Copy: No
var clone = { ...customer };
alert(clone.name + ' ' + clone.example.type); // David DavidType
//alert(clone.greet()); // Not OK
clone.name = 'Steve';
clone.example.type = 'SteveType';
alert(customer.name + ' ' + customer.example.type); // David SteveType
Option 2: Object.assign
Properties: Yes
Methods: No
Deep Copy: No
var clone = Object.assign({}, customer);
alert(clone.name + ' ' + clone.example.type); // David DavidType
alert(clone.greet()); // Not OK, although compiler won't spot it
clone.name = 'Steve';
clone.example.type = 'SteveType';
alert(customer.name + ' ' + customer.example.type); // David SteveType
Option 3: Object.create
Properties: Inherited
Methods: Inherited
Deep Copy: Shallow Inherited (deep changes affect both original and clone)
var clone = Object.create(customer);
alert(clone.name + ' ' + clone.example.type); // David DavidType
alert(clone.greet()); // OK
customer.name = 'Misha';
customer.example = new Example("MishaType");
// clone sees changes to original
alert(clone.name + ' ' + clone.example.type); // Misha MishaType
clone.name = 'Steve';
clone.example.type = 'SteveType';
// original sees changes to clone
alert(customer.name + ' ' + customer.example.type); // Misha SteveType
Option 4: Deep Copy Function
Properties: Yes
Methods: No
Deep Copy: Yes
function deepCopy(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = deepCopy(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = deepCopy(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
var clone = deepCopy(customer) as Customer;
alert(clone.name + ' ' + clone.example.type); // David DavidType
// alert(clone.greet()); // Not OK - not really a customer
clone.name = 'Steve';
clone.example.type = 'SteveType';
alert(customer.name + ' ' + customer.example.type); // David DavidType
angular : how can i clone an object in typescript?
Try using
this.objClone = Object.assign({}, this.obj);
you can also use use lodash :
lodash is recommended for lot of objects / array manipulations
possible copy of Cloning objects TypeScript , What's alternative to angular.copy in Angular or What is the most efficient way to deep clone an object in JavaScript?
Cloning a TypeScript Object
.clone() only clones DOM elements. In order to clone JavaScript objects try jQuery.extend. Something like this
// Shallow copy
var newObject = jQuery.extend({}, oldObject);
// Deep copy
var newObject = jQuery.extend(true, {}, oldObject);
Typescript transpiles to JavaScript. So, JavaScript way will work fine.
Demo:
// Transpiled version of TypeScript"use strict"; var Restaurant = (function () { function Restaurant(id, name) { this.id = id; this.name = name; } Restaurant.prototype.getId = function () { return this.id; }; Restaurant.prototype.setId = function (_id) { this.id = _id; }; Restaurant.prototype.getName = function () { return this.name; }; Restaurant.prototype.setName = function (_name) { this.name = _name; }; return Restaurant; }());
// Test Snippetvar r1 = new Restaurant(1, "A");var r2 = jQuery.extend(true, {}, r1);
r2.setName("B");
console.log(r1.name);console.log(r2.name);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Typescript - clone an object and cast into another type
These types of conversions are common for handling API data coming in
Here's how I handle these type of conversions:
types/GithubRepo.ts
export interface GithubRepo {
id: number;
name: string;
description: string;
stars: number;
}
export const convertIntoGithubRepo = (githubRepoFragment: any): GithubRepo => ({
id: githubRepoFragment.id,
name: githubRepoFragment.name,
description: githubRepoFragment.description || '',
stars: githubRepoFragment.stargazers_count,
});
Usage
import { convertIntoGithubRepo } from 'types/GithubRepo';
const githubRepo = convertIntoGithubRepo(someGithubRepoFragment);
For this example, I am defining a type, then defining methods to copy an object of type any
over to a type of GithubRepo
During this conversion, we can see that I'm checking for a descriptions existence on the any
object, and adding a blank if one is falsy
Typescript how to clone object except for one key
What you need is spread operator (check the "Object Spread and Rest" part). Typescript is a superset of JavaScript, both of them support this feature.
const {adminPasswords, ...state} = initialStateFromDB;
const initialState: InitialState = {
...state,
adminPasswords: JSON.parse(adminPasswords)
}
How do I correctly clone a JavaScript object?
2022 update
There's a new JS standard called structured cloning. It works in many browsers (see Can I Use).
const clone = structuredClone(object);
Old answer
To do this for any object in JavaScript will not be simple or straightforward. You will run into the problem of erroneously picking up attributes from the object's prototype that should be left in the prototype and not copied to the new instance. If, for instance, you are adding a clone
method to Object.prototype
, as some answers depict, you will need to explicitly skip that attribute. But what if there are other additional methods added to Object.prototype
, or other intermediate prototypes, that you don't know about? In that case, you will copy attributes you shouldn't, so you need to detect unforeseen, non-local attributes with the hasOwnProperty
method.
In addition to non-enumerable attributes, you'll encounter a tougher problem when you try to copy objects that have hidden properties. For example, prototype
is a hidden property of a function. Also, an object's prototype is referenced with the attribute __proto__
, which is also hidden, and will not be copied by a for/in loop iterating over the source object's attributes. I think __proto__
might be specific to Firefox's JavaScript interpreter and it may be something different in other browsers, but you get the picture. Not everything is enumerable. You can copy a hidden attribute if you know its name, but I don't know of any way to discover it automatically.
Yet another snag in the quest for an elegant solution is the problem of setting up the prototype inheritance correctly. If your source object's prototype is Object
, then simply creating a new general object with {}
will work, but if the source's prototype is some descendant of Object
, then you are going to be missing the additional members from that prototype which you skipped using the hasOwnProperty
filter, or which were in the prototype, but weren't enumerable in the first place. One solution might be to call the source object's constructor
property to get the initial copy object and then copy over the attributes, but then you still will not get non-enumerable attributes. For example, a Date
object stores its data as a hidden member:
function clone(obj) {
if (null == obj || "object" != typeof obj) return obj;
var copy = obj.constructor();
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
}
return copy;
}
var d1 = new Date();
/* Executes function after 5 seconds. */
setTimeout(function(){
var d2 = clone(d1);
alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);
The date string for d1
will be 5 seconds behind that of d2
. A way to make one Date
the same as another is by calling the setTime
method, but that is specific to the Date
class. I don't think there is a bullet-proof general solution to this problem, though I would be happy to be wrong!
When I had to implement general deep copying I ended up compromising by assuming that I would only need to copy a plain Object
, Array
, Date
, String
, Number
, or Boolean
. The last 3 types are immutable, so I could perform a shallow copy and not worry about it changing. I further assumed that any elements contained in Object
or Array
would also be one of the 6 simple types in that list. This can be accomplished with code like the following:
function clone(obj) {
var copy;
// Handle the 3 simple types, and null or undefined
if (null == obj || "object" != typeof obj) return obj;
// Handle Date
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
// Handle Array
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = clone(obj[i]);
}
return copy;
}
// Handle Object
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
The above function will work adequately for the 6 simple types I mentioned, as long as the data in the objects and arrays form a tree structure. That is, there isn't more than one reference to the same data in the object. For example:
// This would be cloneable:
var tree = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"right" : null,
"data" : 8
};
// This would kind-of work, but you would get 2 copies of the
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];
// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
"left" : { "left" : null, "right" : null, "data" : 3 },
"data" : 8
};
cyclicGraph["right"] = cyclicGraph;
It will not be able to handle any JavaScript object, but it may be sufficient for many purposes as long as you don't assume that it will just work for anything you throw at it.
typescript how to copy object without references
You need a deep copy, example:
let newItem = Json.parse(JSON.stringify(item));
Related Topics
JavaScript - How to Get the Url of Script Being Called
Npm Install Cannot Find Module 'Semver'
Finding Element's Position Relative to the Document
Element Not Visible Error (Not Able to Click an Element)
How to Declare a Global Variable in a .Js File
How to Implement Prepend and Append with Regular JavaScript
Uncaught Typeerror: Cannot Read Property 'Value' of Undefined
How to Require a Controller in an Angularjs Directive
Watch Multiple $Scope Attributes
Firebase Query Methods Startat() Taking Case Sensitive Parameters
How to Smoothly Scroll to an Element in Pure JavaScript
Injecting JavaScript Variable Before Content Script
Triggering a JavaScript Click() Event at Specific Coordinates