'How to check and narrow a type with isNull instead of isNonNullable in TypeScript

I would like to universally type guard entities before returning them from an API. I want to make sure that anything that is null | undefined throws an HttpStatus.NOT_FOUND and thus I wanted to do something like this:

export const entityOr404 = <T>(entity: T | undefined): NonNullable<T> => {
  if (entity != null) {
    throw new HttpException('Not Found', HttpStatus.NOT_FOUND)
  }
  return entity
}

But in this case, TypeScript cannot understand that entity is NonNullable when returning (Type 'T' is not assignable to type 'NonNullable<T>'.). I also don't want to use the non-null assertion because I'd have to disable the eslint rule for this line. So I thought of doing something like this instead:

const isNonNullable = <T>(
  entity: T | null | undefined
): entity is NonNullable<T> => {
  return entity != null
}


export const entityOr404 = <T>(entity: T | undefined): NonNullable<T> => {
  if (!isNonNullable(entity)) {
    throw new HttpException('Not Found', HttpStatus.NOT_FOUND)
  }
  return entity
}

What I don't like about this solution is that I have to negate it and I think it would be more clear to have isNull instead of !isNonNullable and thus I'd like to write something like:

type Nullable = undefined | null
const isNull = (entity: unknown): entity is Nullable => {
  return entity == null
}

But then I'm of course back to TypeScript being confused and throwing Type 'T' is not assignable to type 'NonNullable<T>'. when trying to return the entity. Is there a way for me to express the isNonNullable as its opposite? Somehow let TypeScript know that if !isNull(entity) then entity is NonNullable?



Solution 1:[1]

An assertion is the way !

const isNonNullable = <T>(
  entity: T | null | undefined
): entity is NonNullable<T> => {
  return entity != null
}


function assertEntity<T,>(entity: T): asserts entity is NonNullable<T> {
  if (isNonNullable(entity != null)) {
    throw new Error('My Error')
  }
}

const entityOr404 = <T>(entity: T): NonNullable<T> => {
  assertEntity(entity)
  return entity
};

entityOr404(null) // never
entityOr404(5)    // number
entityOr404({})   // object

Playground

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Matthieu Riegler