kyle_aoki
kyle_aoki

Reputation: 307

How to use a Class Decorator in Typescript to modify all of the Classes' Static Methods?

Let's say I have a class with many static methods. My goal is to wrap each static method with a function. Specifically, I want to catch async errors by applying .catch to each static method like so:

// In user-response.ts

const catchAsyncError = (func: Function) => {
  const catcher = (req: Request, res: Response, next: NextFunction) => {
    func(req, res, next).catch(next);
  }
  return catcher;
}

class UserResponse {
  static createUser = catchAsyncError(createUser);
  static updateUser = catchAsyncError(updateUser);
  static deleteUser = catchAsyncError(deleteUser);
  // more static methods...
}


// In routes.ts

const router = express.Router();

router.post('/create-user', UserResponse.createUser);
router.patch('/update-user', UserResponse.updateUser);
router.delete('/delete-user', UserResponse.deleteUser);

The overarching goal of doing this is to avoid code repetition. Notice how I have to write catchAsyncError(...) repeatedly for each static method.

Also, the purpose of placing these functions in a class is to add some semantic context to each function. This way, a developer who is not familiar with the implementation of the various user related functions will know that they are related because they will read UserResponse.createUser instead of createUser.

I'm looking for a solution like this:

// In user-response.ts

const catchAsyncError = (func: Function) => {
  const catcher = (req: Request, res: Response, next: NextFunction) => {
    func(req, res, next).catch(next);
  }
  return catcher;
}

@withCatchAsyncError
class UserResponse {
  static createUser = createUser;
  static updateUser = updateUser;
  static deleteUser = deleteUser;
  // more static methods...
}

How would I go about implementing a solution like this? Hopefully it's possible, for it is far more elegant and aesthetic than the former.

Upvotes: 1

Views: 539

Answers (1)

kdau
kdau

Reputation: 2099

Your withCatchAsyncError class decorator will be getting the class constructor at runtime without detailed type information on it. Static class members are actually properties of the constructor, so the decorator will need to go through the constructor's properties, look for those that are themselves functions, and wrap them with catchAsyncError. Something like this:

function withCatchAsyncError(constructor: Function) {
  for (const key of Object.keys(constructor)) {
    if (typeof constructor[key] === 'function') {
      constructor[key] = catchAsyncError(constructor[key]);
    }
  }
}

Upvotes: 2

Related Questions