It looks like you're confusing your typings (i.e. from TypeScript) with your runtime code (i.e. JavaScript), and I think a big help for questions like this is understanding when one starts and the other ends. Remember, TS is really just meant to help you while developing. All of its typings and other special syntax might as well just be comments because your machine never sees them. In fact, after it compiles down to JS (which is the code that actually ends up running on your machine), it's usually going to look almost identical to how you wrote it, just with all the typings, generics, and other TS-specific tokens removed.
Speaking of which, here's what your code looks like when compiled down:
// my-file.js
export function InitializeDefaultModelObject() {
const root = {};
Object.keys(root).forEach(key => {
if (typeof root[key] === 'object')
root[key] = {};
});
return root;
}
// file-that-imported-my-file.js
this.ProjectModel = InitializeDefaultModelObject();
console.table(this.ProjectModel);
Especially if you already have experience with regular JS, the lack of generics or typings here may help you better understand why things aren't working. As is, this function neither expects nor is given any parameters, and as a result, it's always going to have the same output (i.e. an empty object).
However, writing a highly scalable solution that works for objects of any type across all scenarios is not straightforward unless your use case is extremely limited. For example, here's an attempt that kinda-sorta-maybe works based on your original code (in JS, which means it works in TS too even if the compiler complains):
export function InitializeDefaultModelObject(originalObj) {
var sortaCopiedObj = {};
Object.keys(originalObj).forEach(key => {
var typeOfField = typeof originalObj[key];
if (typeOfField === 'object') sortaCopiedObj[key] = {};
});
return sortaCopiedObj;
}
This will work if and only if the objects you're passing in are literals (i.e. so they don't have a prototype to worry about inheriting), you don't care about the value of non-object fields, and you don't care about the nested fields of the object properties that you do copy. For example:
var obj1 = { a: {} };
var copy1 = InitializeDefaultModelObject(obj1); // { a: {} } You probably expect this
var obj2 = { a: { innerProperty: {} }, b: "hey!" };
var copy2 = InitializeDefaultModelObject(obj2); // { a: {} } But did you expect this?
To fix that, you can add an else statement to your function to account for values other than objects, and you can also recursively call it to continue building nested object properties. However, that doesn't solve things if prototypes are involved, and I'm concerned you might not be fully aware of that. If you really only want to use this function to copy object literals of arbitrary depth, cool, then go ahead and add those fixes I just mentioned, but there may be an easier way. If you're going to instantiate an object of known structure multiple times... why not just make that object into a class?
class MyClass {
a = { innerProperty: {} };
b = "hey!";
}
// All of these have the same properties, but they're all unique instances
var x = new MyClass();
var y = new MyClass();
var z = new MyClass();