Tips for validating and narrowing a type in TypeScript using isNull instead of isNonNullable

I want to implement a universal type guard for entities returned from an API. The goal is to handle any null | undefined values by throwing an HttpStatus.NOT_FOUND error. Initially, I tried the following approach:

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

However, TypeScript raised an error (

Type 'T' is not assignable to type 'NonNullable<T>'.
) because it couldn't recognize that entity was NonNullable in the return statement. To address this issue, I came up with a different solution:

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
}

Although this solution works, I find having to negate the function cumbersome. I believe having a isNull function instead of !isNonNullable would be clearer. So, I attempted the following modification:

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

Unfortunately, this led to TypeScript complaining again with the same error message (

Type 'T' is not assignable to type 'NonNullable<T>'.
). Is there a way to express the opposite of isNonNullable and help TypeScript understand that if !isNull(entity), then entity is NonNullable?

Answer №1

An assertion is a powerful tool!

const checkNonNull = <T>(
  item: T | null | undefined
): item is NonNullable<T> => {
  return item !== null && item !== undefined;
}

function validateItem<T,>(item: T): asserts item is NonNullable<T> {
  if (!checkNonNull(item)) {
    throw new Error('Custom Error');
  }
}

const itemOrError = <T>(item: T): NonNullable<T> => {
  validateItem(item);
  return item;
};

itemOrError(null); // never
itemOrError(5);    // number
itemOrError({});   // object

Playground

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Connection to mongo is currently unavailable for Middleware next

This code snippet shows a middleware for Next, which is designed to read the subdomain and check if it exists in the database. import { getValidSubdomain } from '@/lib/getValidSubdomain'; import { NextResponse } from 'next/server' impor ...

Tips for accessing an item from a separate TypeScript document (knockout.js)

In the scenario where I need to utilize an object from another TypeScript file, specifically when I have an API response in one.ts that I want to use in two.ts. I attempted exporting and importing components but encountered difficulties. This code snippe ...

Tips for utilizing the Fluent UI Northstar Color Palette

When working with Fluent UI Northstar, one helpful feature is the color palette. While the documentation provides a list of color names and gradients that can be found here, it can be confusing on how to actually utilize these values (such as 100, 200, e ...

Bidirectional data binding on the table

I am struggling to implement two-way data binding in a table between my .ts and my .html files. I have a structure that adds a new equipment to the array, and I want that new equipment to display on the table within the same screen. I believe it involves ...

Unleashing the full potential of Azure DevOps custom Tasks in VS Code and TypeScript: A guide to configuring input variables

Scenario: I have developed a custom build task for Azure DevOps. This task requires an input parameter, param1 The task is created using VS Code (v1.30.1) and TypeScript (tsc --version state: v3.2.2) Issue During debugging of the task, I am unable to pr ...

Is there a way to check if a date of birth is valid using Regular Expression (RegExp) within a react form?

const dateRegex = new RegExp('/^(0[1-9]|1[012])[- /.](0[1-9]|[12][0-9]|3[01])[- /.] (19|20)\d\d+$/') if (!formData.dob || !dateRegex.test(formData.dob)) { formErrors.dob = "date of birth is required" ...

Creating valuable properties in TypeScript is a skill that requires knowledge and practice

In TypeScript, there is a unique feature available for defining properties with values using the `value` keyword. class Test { constructor(private value: number = 123) { } public MyValueProperty: number = 5; } Here is how you can define such ...

Intellisense capabilities within the Gruntfile.js

Is it a feasible option to enable intellisense functionality within a Gruntfile? Given that 'grunt' is not defined globally but serves as a parameter in the Gruntfile, VSCode may interpret it as an unspecified function parameter 'any'. ...

How to effectively manage errors in TypeScript using RxJS?

Exploring subscribe arguments in the official RxJS documentation has raised some interesting questions for me. For instance, what is the purpose of using error: (e: string) => { ... } to catch errors emitted by an observable? Despite this implementation ...

Incorporating Precision to Decimal Numbers in TypeScript Angular

Having some trouble with this issue and I've tried various solutions without success. This problem is occurring within an Angular project. The requirement is to always display a percentage number with two decimal places, even if the user inputs a who ...

Transform Typescript into compiled css files without using any additional tools

Currently, I am working on a monorepo project where the main project relies on another project called components. When running the entire monorepo, the main project utilizes webpack.dev while the components project simply uses the TypeScript compiler. It l ...

What is the best way to integrate ContextualMenu with Persona in Office Fabric UI React?

Currently, I am utilizing Office Fabric UI React to work on a project. My goal is to implement a ContextualMenu in conjunction with the Persona object. In this particular example, I am demonstrating how ContextualMenu can be applied directly to differ ...

Angular 2 validation issue not functioning as anticipated

Here is a validator function that I have: export const PasswordsEqualValidator = (): ValidatorFn => { return (group: FormGroup): Observable<{[key: string]: boolean}> => { const passwordCtrl: FormControl = <FormControl>group.contr ...

Is it possible to convert a type to a JSON file programmatically?

Recently, I have been tasked with implementing configuration files for my system, one for each environment. However, when it came time to use the config, I realized that it was not typed in an easy way. To solve this issue, I created an index file that imp ...

Does the React memo function modify the component's prop type?

I've come across a strange issue where defining two components causes compilation errors when written separately but not when written in the same file. test3.tsx import React from "react"; type ValueType = number[] | string[] | number | st ...

having difficulty sending a post request with Angular

Submitting form data via HTTP post will look like this: saveDataFile(mutlidata,id,value): Observable<Response> { var _url = 'http://xxxx.xxx.xxx'; var saveDataURL = _url + '/' + id; var _this = this; ...

typescript - transforming text into numerical values

newbalance = (Number(this.balance)) + (Number(this.pastAmount)); The result for my newbalance calculation is coming back as undefined, even though this.balance is 34 and this.pastAmount is 23. I've set this up in the controller and I'm trying t ...

how to implement dynamic water fill effects using SVG in an Angular application

Take a look at the code snippet here HTML TypeScript data = [ { name: 'server1', humidity: '50.9' }, { name: 'server2', humidity: '52.9', }, { name: 'server3', humidity: ...

Attempting to create a function that can accept two out of three different types of arguments

I am trying to create a function that only accepts one of three different types type A = 'a' type B = 'b' type C = 'c' The function should accept either type A, C, or both B and C, but not all three types. This is what I hav ...

Backend data not displaying on HTML page

I am currently working on an Angular 8 application where I have a service dedicated to fetching courses from an API endpoint. The service method that I'm using looks like this: loadCourseById(courseId: number) { return this.http.get<Cours ...