I'm attempting to write a hook that accepts multiple keys of an object and filters down the data based on currently applied filters. So, something along the lines of this:
interface IFilterable<T,K extends keyof T & string>{
key: K;
customFilter?: (item: T[K], applied: T[K][]) => boolean
//The hook uses some default logic to filter if this function isn't available.
}
function useFilters<T,K extends keyof T & string>(data: T[], filters: IFilterable<T,K>[]): T[]{
//filter out the data, return new dataset.
}
The issue is that when multiple filters (IFilterable) are passed in, the item and applied parameters in the customFilter function becomes the union of the types of the keys. Or, in code...
useFilters(
[{a: 1, b: "bob"], {a: 2, b: "berry"}],
[{key: "a", customFilter: (item, applied) => {
//Wanted: item has the type number
//Reality: item has the type (string | number)
},
{key: "b", customFilter: (item, applied) => {
//Wanted: item has the type string.
//Reality: item has the type (string | number)
}
}]);
I have "solved" the problem by typing everything as unknown(or unknown[]) and then creating wrapper functions that cast things back to the proper type later, but I want to keep things strongly typed the whole way through if possible.
So, is there any way in cases like this, where I am passing in array of objects where one property is a key and another property is a function that takes T[K], to narrow to the type of a specific T[K] instead of the union type?
Thanks.