Problem:
Predicate combinators create too many variables, so it's easy to get lost between these functions. If we use the combinator predicate function only once, then it's better to have something more generic.
const isWriter = (user: User) => user.role === UserRole.Writer;
const isEditor = (user: User) => user.role === UserRole.Editor;
const isGreaterThan17 = (user: User) => user.age > 17;
const isGreaterThan5 = (user: User) => user.age > 5;
const greaterThan17AndWriterOrEditor = users.filter(
and(isGreaterThan17, or(isWriter, isEditor))
);
const greaterThan5AndSubscriberOrWriter = users.filter(
and(isGreaterThan5, isWriter)
);
Solution:
We have to start using the combinator predicate factories. Let's add a few:
const isRole = (role: UserRole) =>
(user: User) => user.role === role;
const isGreaterThan = (age: number) =>
(user: User) => user.age > age;
const greaterThan17AndWriterOrEditor = users.filter(
and(isGreaterThan(17), or(isRole(UserRole.Writer), isRole(UserRole.Editor)))
);
const greaterThan5AndSubscriberOrWriter = users.filter(
and(isGreaterThan(5), isRole(UserRole.Writer))
You've probably noticed that some function invocation is repeated with the same arguments. The best option will be to mix predicate factories with combinator predicates together. Thus we'll have the best of both worlds.
const isRole = (role: UserRole) =>
(user: User) => user.role === role;
const isGreaterThan = (age: number) =>
(user: User) => user.age > age;
const isWriter = isRole(UserRole.Writer)
const greaterThan17AndWriterOrEditor = users.filter(
and(isGreaterThan(17), or(isWriter, isRole(UserRole.Editor)))
);
const greaterThan5AndSubscriberOrWriter = users.filter(
and(isGreaterThan(5), isWriter)
);
In this way, we do fewer repeats and keep the code clean and neat.