I need to build a function that takes 0 or more keys from a given object and returns a record with those given keys only.
I know, however, how to request a parameter that is an array of keys from an object:
function myFunc<T>(field: (keyof T)[]): void {}
Problem is that I'm not sure how to make it so that instead of returning void, it returns a record with the given fields as keys:
function myFunc<T, K extends (keyof T)[]>(fields: K): Record<K, number> {}
This yields Type 'K' does not satisfy the constraint 'string | number | symbol'.
which is fair.
To make it clearer, this would be the implementation of this function in javascript:
function myFunc(fields) {
return fields.reduce((result, key, idx) => ({...result, [key]: idx}), {});
}
And this would be an example of the input and expected output for that function:
type Person = {
name: string,
age: number,
citizen: boolean
}
console.log(myFunc<Person>(['name', 'citizen'])); // Object { name: 0, citizen: 1 }
console.log(myFunc<Person>(['name', 'years'])); // Error: Argument of type '"years"' is not assignable to parameter of type '"name" | "age" | "citizen"'
It looks like it should be a very trivial thing that I'm missing completely but I can't find the solution even by searching on internet 😔
Thanks in advance!
Consider this example:
type Person = {
name: string,
age: number,
citizen: boolean
}
type Result<Fields extends PropertyKey[]> = {
[Prop in keyof Fields as Fields[Prop] extends Fields[number] ? Fields[Prop] : never]: Exclude<Prop, number>
}
function withObj<Obj,>(obj: Obj): <Field extends keyof Obj, Fields extends Field[]>(fields: [...Fields]) => Result<Fields>
function withObj<Obj,>(obj: Obj) {
return (fields: string[]) =>
fields.reduce((result, key, idx) => ({ ...result, [key]: idx }), {});
}
const myFunc = withObj({
name: 'John',
age: 42,
citizen: true
})
const x = myFunc(['name', 'citizen']) // ok
x.name // 0
const y = myFunc(['name', 'years']) // error
Result
iterates through each array prop and check whether Fields[index] is a subtype of array elements. If yes - rename array key to appropriate array element and use index in a place of a value.
Also, I have added curried version of myFunc
just for the proper inference.