I think I will make a Reusable function series as a way for me to store small functions that I have come across many times as it will be useful to look back in the future.
This time it's the filterBy
function using generic type.
The function:
function filterBy<T, K extends keyof T, V extends T[K]>(array: T[], property: K, value: V): T[] {
return array.filter(item => item[property] === value)
}
The params
(array: T[], property: K, value: V)
This function accepts array of objects and the objects can have different structures, then it will take one of the property to filter with the given value.
The output
:T[]
If you look closely to the function there, you might notice the return value is T[]
, so it will also return an array with type of T
which is the same with the input array.
Example
interface Dog {
name: string
breed: string
age: number
}
const dogs: Dog[] = [
{name: 'Foo', breed: 'Labradoodle', age: 10},
{name: 'Tom', breed: 'Corgi', age: 4},
{name: 'Fazi', breed: 'Husky', age: 5},
{name: 'Luck', breed: 'Corgi', age: 3},
]
I don't really need to add the interface Dog
, TypeScript will automatically interpret it, but just to make it clear.
So for example to filter out this dogs
array to only return corgi breed type of dogs, we would do like this:
const onlyCorgis = filterBy(dogs, 'breed', 'Corgi')
But why using generic type here, what's the point?
It will be useful for the case that when the prop name has a typo or the value is not in correct type. For example, these cases are the invalid cases, and typescript will complain:
const wrongProperty = filterBy(dogs, 'bree', 'Corgi');
// Will fail with: "Argument of type '"bree"' is not assignable to parameter of type 'keyof Dog'"
const wrongValue = filterBy(dogs, 'breed', 5);
// Will fail with: "Argument of type 'number' is not assignable to parameter of type 'string'."
const anotherWrongValue = filterBy(dogs, 'age', '4');
// Will fail with: "Argument of type 'number' is not assignable to parameter of type 'string'."
That's it for this filterBy
function!