Lets say we have a class with a property that references another class. I want to be able to deep clone an "Immutable" (or Readonly
) instance of it:
import * as _ from 'lodash';
interface IParent{
name: string;
}
interface IChild{
name: string;
parent: IParent;
}
public class Child implements IChild{
public name: string;
public parent: string;
constructor(props: IChild){
Object.assign(this, props);
}
toImmutable(): Readonly<IChild> {
return _.cloneDeep(this); //lodash deep clone
}
}
While this code makes the first class properties on the child
instance Readonly
, the referenced object can still be edited:
let child = new Child({
name: 'abc',
parent: { name: 123 }
});
let immutable = child.toImmutable();
immutable.name = 'abc'; //not allowed
immutable.parent.name = 'abc'; //this is allowed!!!
Is there an elegant solution that would allow me to make EVERYTHING on the cloned object readonly?
Note:
Looks like lodash
has a method called cloneDeepWith
that takes a "customizer"... Wondering if this might be going in the right direction.
The key is to create a custom type DeepReadonly<T>
that you would use instead of Readonly<>
:
type DeepReadonly<T> = {
readonly [K in keyof T]: DeepReadonly<T[K]>;
}
This type will recursively apply readonly attribute to all nested objects.
type DeepReadonly<T> = {
readonly [K in keyof T]: DeepReadonly<T[K]>;
}
import * as _ from 'lodash';
interface IParent{
name: string;
}
interface IChild{
name: string;
parent: IParent;
}
class Child implements IChild{
public name: string;
public parent: IParent;
constructor(props: IChild){
Object.assign(this, props);
}
toImmutable(): DeepReadonly<IChild> {
return _.cloneDeep(this); //lodash deep clone
}
}
let child = new Child({
name: 'abc',
parent: { name: "123" }
});
let immutable = child.toImmutable();
immutable.name = 'abc'; //not allowed
immutable.parent.name = 'abc'; //not allowed