Uncalled Function Checks

Playground

A common and dangerous error is to forget to invoke a function, especially if the function has zero arguments or is named in a way that implies it might be a property rather than a function.

  1. interface User {
  2. isAdministrator(): boolean;
  3. notify(): void;
  4. doNotDisturb?(): boolean;
  5. }
  6. // later...
  7. // Broken code, do not use!
  8. function doAdminThing(user: User) {
  9. // oops!
  10. if (user.isAdministrator) {
  11. sudo();
  12. editTheConfiguration();
  13. }
  14. else {
  15. throw new AccessDeniedError("User is not an admin");
  16. }
  17. }

Here, we forgot to call isAdministrator, and the code incorrectly allows non-adminstrator users to edit the configuration!

In TypeScript 3.7, this is identified as a likely error:

  1. function doAdminThing(user: User) {
  2. if (user.isAdministrator) {
  3. // ~~~~~~~~~~~~~~~~~~~~
  4. // error! This condition will always return true since the function is always defined.
  5. // Did you mean to call it instead?

This check is a breaking change, but for that reason the checks are very conservative.This error is only issued in if conditions, and it is not issued on optional properties, if strictNullChecks is off, or if the function is later called within the body of the if:

  1. interface User {
  2. isAdministrator(): boolean;
  3. notify(): void;
  4. doNotDisturb?(): boolean;
  5. }
  6. function issueNotification(user: User) {
  7. if (user.doNotDisturb) {
  8. // OK, property is optional
  9. }
  10. if (user.notify) {
  11. // OK, called the function
  12. user.notify();
  13. }
  14. }

If you intended to test the function without calling it, you can correct the definition of it to include undefined/null, or use !! to write something like if (!!user.isAdministrator) to indicate that the coercion is intentional.

We owe a big thanks to GitHub user @jwbay who took the initiative to create a proof-of-concept and iterated to provide us with with the current version.