Search code examples
typescripttype-narrowing

How do I narrow a value to *not* be undefined?


Right now I have this code which works...

(err, resp) => {
  if (resp) {
    if (resp.missing) {
      resolve(new CacheListFetch.Miss());
    } else if (resp.found) {
      resolve(new CacheListFetch.Hit(resp.found.values));
    } else {
      resolve(new CacheListFetch.Error(cacheServiceErrorMapper(err)));
    }
  } else {
    resolve(new CacheListFetch.Error(cacheServiceErrorMapper(err)));
  }
}

I'm trying to get rid of that redundant error clause. I tried this...

(err, resp) => {
  switch (true) {
    case typeof resp === 'undefined':
    default:
      resolve(new CacheListFetch.Error(cacheServiceErrorMapper(err)));
      break;
    case resp.missing instanceof
      cache_client._ListFetchResponse._Missing:
      resolve(new CacheListFetch.Miss());
      break;
    case resp.found instanceof cache_client._ListFetchResponse._Found:
      resolve(new CacheListFetch.Hit(resp.found.values));
      break;
  }
}

eslint keeps saying resp is possibly undefined, yet that's not possible.

src/internal/cache-client.ts:303:18 - error TS18048: 'resp' is possibly 'undefined'.

303             case resp.missing instanceof
                     ~~~~

src/internal/cache-client.ts:307:18 - error TS18048: 'resp' is possibly 'undefined'.

307             case resp.found instanceof cache_client._ListFetchResponse._Found:
                     ~~~~

src/internal/cache-client.ts:308:46 - error TS18048: 'resp' is possibly 'undefined'.

308               resolve(new CacheListFetch.Hit(resp.found.values));
                                                 ~~~~

How can I DRY up that nested if/else?


Solution

  • You can use the optional chaining operator (?.) in your conditions:

    (err, resp) => {
      if (resp?.missing) {
        resolve(new CacheListFetch.Miss());
      } else if (resp?.found) {
        resolve(new CacheListFetch.Hit(resp.found.values));
      } else {
        resolve(new CacheListFetch.Error(cacheServiceErrorMapper(err)));
      }
    }
    

    This allows you to test the existence of resp and the existence of resp.missing in a single statement.

    If you end up at else it's because:

    • resp is falsy.
    • or resp exists and resp.missing and resp.found are both falsy.