You can utilize classes to achieve this functionality as they act as types.
Here is a more advanced solution:
class Person {
name = ''
}
class PersonWithId extends Person {
id = ''
// add any other properties you need
}
Next, you can create a function to clean objects like so:
function clearObject<T>(C: any, o: any, f: any = {} ): T {
Object.keys( C ).forEach( ( key: any ) => {
const keyType = typeof C[ key ]
const Value = o[ key ] ?? C[ key ]
if (
!!Value &&
keyType === 'object' &&
Object.keys( Value ).length > 0
) {
// if the key is another object
if ( !Array.isArray( Value ) ) {
f[ key ] = clearObject( C[ key ], Value )
return f
}
// if the key is an array
Value.forEach( ( items: any ) => {
f[ key ] = clearObject( C[ key ], items, f[ key ] )
return f
} )
}
// assign the value to its key
f[ key ] = Value
} )
// return the new object cleaned and typed
return f as T
}
This function will iterate through each key of the type class and extract only keys that exist in your type class. It then creates a new object and returns it with the correct type.
You can use it like this:
const person: PersonWithId = {
name: 'John',
id: 1
}
const cleared = clearObject<Person>(new Person(), person)
Now, note that the cleared
variable is of type Person, and if you attempt to access the ID property from it, TypeScript will show an error.
This method works for clearing any object based on another type class without encountering any TypeScript errors.
I have provided a working example on CodeSandbox. Alternatively, if you do not require nested object key validation, you can opt for this simplified solution:
// define classes as types
class PublicPerson {
name = "";
age = 99;
}
class PrivatePerson extends PublicPerson {
id = "";
hash = "";
}
// object cleaning function
function clearObject<T>(newType: T, current: any): T {
const cleared: any = {};
Object.keys(newType).forEach((key) => {
// assign the value to its key
// if the key does not exist in the current object
// we get the value from newType
// e.g., if the current object lacks the 'age' property,
// the assigned value will be '99'
// as it's the default value for the class
cleared[key] = current[key] ?? newType[key];
});
// return the new object cleaned and typed
return cleared as T;
}
// your object
const person: PrivatePerson = {
name: "Person Name",
age: 18,
id: "123",
hash: "aPrivatePasswordHash"
};
// cleaned object
// this is useful on the backend if you wish to
// send a person's data to a client
// you'll always send cleaned public data
const cleaned = clearObject<PublicPerson>(new PublicPerson(), person);