Calling JSON.serialize and then JSON.parse on a value is the fastest general-purpose way to deeply clone an object in Javascript. However, the downside is that it only works on values for which x => JSON.parse(JSON.serialize(x)) is the identity function. In other words, this function will only work correctly on strings, numbers, booleans, null, undefined, and arrays and objects built off of these types.
It's common to choose a function name or leave comments warning about this constraint. However, in Typescript we can enforce this constraint at the type level using a recursive type:
// note that the primitives Symbol and BigInt are not serializable// this is also the same as the JSON type from Typescript's docs, but with the// addition of undefined as a valid value:// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#more-recursive-type-aliasestypeFastClonable=|string|number|boolean|null|undefined|FastClonable[]| { [x:string]:FastClonable }constfastClone= <TextendsFastClonable>(x:T):T = JSON.parse(JSON.serialize(x))
Now, when passing any value that will not be transparently cloned via this function, Typescript will emit an error.
typeFooType= { bar: { baz:number }}interfaceFooInterface { bar: { baz:number }}constmyFoo= { bar: { baz:123 }}// correctly returns a value with type FooTypefastClone(myFoo asFooType)// Argument of type 'FooInterface' is not assignable to parameter of type 'FastClonable'.// Type 'FooInterface' is not assignable to type '{ [x: string]: FastClonable; }'.// Index signature is missing in type 'FooInterface'.fastClone(myFoo asFooInterface)