I have this sloppy, hacked together function in JavaScript that allows you to pick properties from an object using dot notation:
const pickObjProps = (obj,paths)=>{
let newObj = {}
paths.forEach((path)=>{
const value = path.split('.').reduce((prev,curr)=>{
return prev ? prev[curr] : null;
}
, obj || self);
function buildObj(key, value) {
var object
var result = object = {};
var arr = key.split('.');
for (var i = 0; i < arr.length - 1; i++) {
object = object[arr[i]] = {};
}
object[arr[arr.length - 1]] = value;
return result;
}
newObj = Object.assign(newObj, {
...buildObj(path, value)
})
}
)
return newObj
}
const obj = {
primaryEmail: "mocha.banjo@somecompany.com",
suspended: false,
id: 'aiojefoij23498sdofnsfsdfoij',
customSchemas: {
Roster: {
prop1: 'val1',
prop2: 'val2',
prop3: 'val3'
}
},
some: {
deeply: {
nested: {
value: 2345945
}
}
},
names: {
givenName: 'Mocha',
familyName: 'Banjo',
fullName: 'Mocha Banjo'
},
phones: [{
type: 'primary',
value: '+1 (000) 000-0000'
}]
}
const result = pickObjProps(obj, ['primaryEmail', 'customSchemas.Roster', 'some.deeply.nested',])
console.log(result)
The function works as I intend it to. However, I want to type the function in TypeScript and am having a hell of a time.
I stumbled across another post which gave me some insight on how to possibly type it:
type PickByDotNotation<TObject, TPath extends string> =
TPath extends `${infer TKey extends keyof TObject & string}.${infer TRest}` ?
PickByDotNotation<TObject[TKey], TRest> :
TPath extends keyof TObject ?
TObject[TPath] :
never
In trying to type the function, I am trying to create a new type called PickManyByDotNotation
that takes two generic arguments:
- An Object
- A array of strings
This is as far as I have gotten:
type PickManyByDotNotation<TObject, TPaths extends string[]> = TPaths extends [
infer TKey extends string,
infer TRest extends string[],
]
? PickManyByDotNotation<PickByDotNotation<TObject, TKey>, TRest>
: TPaths extends string
? PickByDotNotation<TObject, TPaths>
: never
type PickByDotNotation<TObject, TPath extends string> =
// Constraining TKey so we don't need to check if its keyof TObject
TPath extends `${infer TKey extends keyof TObject & string}.${infer TRest}`
? PickByDotNotation<TObject[TKey], TRest>
: TPath extends keyof TObject
? TObject[TPath]
: never
The idea would be to use the type as such:
interface Test {
customer: {
email: string;
name: string;
phone: string;
id: string
};
};
type PickMany = PickManyByDotNotation<Test, ['customer.email', 'custom.name']>
// which would theoretically return something like:
//
// customer: {
// email: string
// name: string
// }
I am pulling my hair out at this point, and am actually very embarrassed to be posting.
If you could help me finish the type PickManyByDotNotation
and or possibly give me some insight on how to property type the function, I would be more than grateful.