After a few hours of research for a good logical and semantic prototype, i found a good solution.

    router.put('/:articleId', AuthGuard([{ role: 'user', when: isOwner }, { role: 'admin' }]), async (req, res) => {
    
    });

A predicate function can be added to a specific role for more flexibility. In this example, the user can only update an item "when" it owns it.

The little module :

    import passport from 'passport';
    import { Request, Response, NextFunction } from 'express';
    
    export const AuthenticateGuard = passport.authenticate('jwt', { session: false });
    
    export interface IAuthorizeItem {
      role: string;
      when?: (req: Request) => boolean;
    };
    
    export default function AuthGuard(items?: IAuthorizeItem[]) {
      return [AuthenticateGuard, AuthorizeGuard(items)];
    };
    
    export function AuthorizeGuard(items?: IAuthorizeItem[]) {
      return async function (req: Request, res: Response, next: NextFunction) {
        let user = req.user;
        if (!user) {
          return res.status(500).send('Access not allowed !');
        }
    
        if (typeof req.user.hasRole !== 'function') {
          throw new Error('User object must have hasRole method');
        }
    
        if (!items || items.length == 0) {
          return next();
        }
    
        for (let item of items) {
          if (req.user.hasRole(item.role)) {
            if (!item.when) {
              return next();
            }
            else {
              if (item.when(req)) {
                return next();
              }
            }
          }
        }
    
        res.status(500).send('Access not allowed !');
      }
    };

Note: I avoid any dynamic types checking because i used typescript and this module must be used only with static object.