How I understand your need is:
- you have some entity, lets say User
- you want to create a generic Filter type which can be used for different entities
- generic Filter type should be type safe for every entity
First of all lets define possible rules we need to filter
type Rule<T> = { $eq: T } | { $gte: T } // append any other rules you need
I have taken from your example two rules - $eq
and $gte
but by |
you can add others you need.
Second lets define generic Filter
type
type Filter<Obj> = {
[K in keyof Obj]: Rule<Obj[K]>
}
Our type says - I should have all keys of given object, and for every key I should define a rule which works on the same type as this property has. So for property a: string
the rule needs to be or {$eq: string}
or {$gte: string}
.
Lets see an example:
// example filter for the User type
type User = {
id: number;
name: string;
}
// create an instance of the filter for the User entity
const userFilter: Filter<User> = {
id: { $gte: 1 }, // rule needs to have value as number
name: {$eq: '2'} // rule needs to have value as string
}
// what if we want to filter by only part of the interface - use Pick utility type
const userFilter2: Filter<Pick<User, 'name'>> = {
name: {$eq: '2'} // only name is needed to be provided
}
Type Filter
is fully type safe, by creating instance of this type we need to define proper keys and proper rules for these keys.
As an addition you can conditionally say which rules are possible for which types. Lets say $gte
can be applicable only for numbers, but not for other types. You can do so by:
type Rule<T> = T extends number ? { $eq: T } | { $gte: T } : { $eq: T }
Above definition will prevent from using $gte
for anything other than number.