safeguards for potential parameters

Below is a fixture file with optional properties that I have type guarded:

Fixture File-

 {
      "profiles": [
        {
          "name": "Laakea",
          "phoneNumber": "2033719225",
          "authGroupName": "Drivers"
        },
        {
          "name": "Lkhagvasuren",
          "phoneNumber": "2033719225",
          "authGroupName": "Drivers"
        },
        {
          "name": "Joaquin",
          "phoneNumber": "2033719225"
        }
      ]
    }

Type Interface-

 export interface Profile {
      name: string;
      authGroupName?: string;
      phoneNumber?: string;
      email?: string;
    }

Type Guard Function-

export function isValidProfiles(profiles: unknown): profiles is Profile[] {
  if (!Array.isArray(profiles)) {
    return false;
  }
  for (let index = 0; index < profiles.length; index += 1) {
    // Validation logic here
  }

  return true;
}

Instead of multiple if statements, any suggestions to improve the code?

Answer №1

When you have identical code for checking properties like email, phoneNumber, and authGroupName, it's best to refactor them into a single block of code that can be executed multiple times.

One way to do this is by creating an array of keys (defined with a const assertion to ensure the values are explicitly specified as "email", "phoneNumber", and "authGroupName" instead of just string), and then using the every() method to validate every key against every member in the profiles array:

function isValidProfiles(profiles: unknown): profiles is Profile[] {
  if (!Array.isArray(profiles)) { return false; }
  const keys = ["email", "phoneNumber", "authGroupName"] as const;
  return profiles.every(p => p && (typeof p === "object") && (typeof p.name === "string") &&
    keys.every(k => typeof p[k] === "undefined" || typeof p[k] === "string")
  );
}

In this approach, I'm ensuring that for each key k and each profile p, the type of p[k] is either "undefined" or "string". This allows for handling string properties and missing ones while also accepting properties with keys present but explicitly set to undefined. TypeScript inherently considers these optional unless you activate the --exactOptionalPropertyTypes compiler option, which isn't included in the standard --strict settings.


Now, let's put it to the test:

const val = [
  {
    "name": "Laakea",
    "phoneNumber": "2033719225",
    "authGroupName": "Drivers"
  },
  {
    "name": "Lkhagvasuren",
    "phoneNumber": "2033719225",
    "authGroupName": "Drivers"
  },
  {
    "name": "Joaquin",
    "phoneNumber": "2033719225"
  }
];
if (isValidProfiles(val)) {
  console.log(val.map(
    x => x.authGroupName ?? "no-auth").join(",")
  ); // "Drivers,Drivers,no-auth" 
} else {
  console.log("nope")
}

The validation of val as a Profile[] by the compiler appears successful, allowing smooth operations.

Access the code on Playground

Answer №2

Here's a solution I devised -

export function checkValidProfiles(profiles: unknown): profiles is Profile[] {
  if (!Array.isArray(profiles)) {
    return false;
  }
  for (let index = 0; index < profiles.length; index += 1) {
    if (
      !(
        typeof profiles[index].name === 'string' &&
        (!profiles[index].phoneNumber || typeof profiles[index].phoneNumber === 'string') &&
        (!profiles[index].email || typeof profiles[index].email === 'string') &&
        (!profiles[index].authGroupName || typeof profiles[index].authGroupName === 'string')
      )
    ) {
      return false;
    }
  }

  return true;
}

This approach may not be as robust and effective as the method created by @jcalz.

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

What is the best way to change between different Angular 2 material tabs using typescript?

I need help with switching tabs using buttons <md-tab-group> <md-tab label="Tab 1">Content 1</md-tab> <md-tab label="Tab 2">Content 2</md-tab> </md-tab-group> <button md-button (click)="showTab1()">Show Tab 1< ...

Incorporating a unique variant with Tailwind called "

I'm currently struggling with inputting the configuration file for Tailwind. Despite my efforts, I cannot seem to get it right. The code appears correct to me, but I am unsure of what mistake I might be making. Below is the snippet of my code: import ...

Can TypeScript identify and eliminate any undefined values within an array effectively?

Is there a simple and efficient method to achieve the following: const x: (string|undefined)[] = ['aaa', undefined, 'ccc']; const y = _.filter(x, it => !!it); in order for TypeScript to correctly identify y as string[], without nee ...

Using Angular's setTimeout() function with an external lambda that includes a parameter

My goal is to tackle two issues at once: 1) using setTimeout( #action#, timeMillis) with #action# as a lambda 2) supplying the lambda with a parameter. The common method of setTimeout( ()=>{ #callback# }, timeMillis) works flawlessly when extracting () ...

Is the stepper's vertical line separating when the label extends onto multiple lines?

I'm facing an issue with the text inside my MaterialUI Stepper // StepLabel, where it sometimes wraps over multiple lines. Is there a way to keep the vertical StepConnectors attached to the StepIcons regardless of the number of lines of text in the l ...

TS2304: Unable to locate variable '_ZonePrivate'

I'm facing an issue while testing my angular 9 application. When I try to run it using ng serve, it seems to be running fine, but I encounter the error message Cannot GET / when attempting to display the page. The same error persists when I build it w ...

Implicated Generic in TypeScript

Is there a way to simplify my class A implementation? export class A<TB extends B<TC>, TC> implements TD<TB, TC> { make(): TC {} } Currently, I have to specify the TC type every time I create an instance of A: class CTest {} class BTes ...

Add TypeScript typings for npm utility manually by incorporating type definitions

I'm in the process of incorporating TypeScript into an existing JavaScript project, and I'm currently facing the challenge of adding typings to an npm module that lacks type definitions, which I anticipate will be a common issue. When I try to i ...

Converting a Promise to an Observable in Angular using Typescript

I have a working method that functions as intended: getdata(): Promise<any> { let query = `SELECT * FROM table`; return new Promise((resolve, reject) => { this.db.query(query, (error, rows) => { if(error) reject(error); ...

Ensure that the method is triggered

I have a builder class that implements an interface which it is expected to build. However, I would like to enforce one method of this class to be called at compile time, rather than runtime. The class is designed to be used as a chain of method calls and ...

Displaying Date in Angular 2 Application with Proper Formatting

I'm currently working on formatting the date pipe in my Angular application to display correctly when used within an input template. Originally, without the date formatting, my code looked like this: <input class="app-input" [readonly]="!hasAdminA ...

Ways to utilize an interface with a blank object that will be filled at a subsequent time

I'm struggling to find the right words to explain my situation. Essentially, I need to create an empty object that I plan to fill with data. I already have a clear idea of what the final structure of this object should be: interface BodyInterface { ...

Refresh a reactive form in Angular Material

I'm facing a challenge with resetting my form after data submission. Everything is working except for validating the email format. Using this.form.reset() doesn't seem to resolve the issue. https://i.sstatic.net/QRZEa.png Any suggestions on how ...

Struggling to successfully deploy a React App to Azure through a Github Actions workflow due to encountering a TypeScript error

I have been searching extensively on SO for the past few days, trying different methods but I just can't seem to make this work. This is not my usual area of expertise as I am a .Net developer and I inherited this project. To get where I am now, I fo ...

Error display in Elastic Apm Rum Angular implementation

Having some issues with incorporating the @elastic/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f5948598d8878098d8949b9280999487b5c7dbc4dbc4">[email protected]</a> package into my project. Angular is throwing console ...

The type 'Store<unknown, AnyAction>' is lacking the properties: dispatch, getState, subscribe, and replaceReducer

I have configured the redux store in a public library as follows: import { configureStore } from '@reduxjs/toolkit'; import rootReducer from '@/common/combineReducer'; import { createLogger } from 'redux-logger'; import thunk ...

Error message: Unable to locate module when utilizing my alternative library packaged with Rollup

Currently, I am utilizing rollup to package a UI library for use across various primary applications. However, the bundled ESM file contains imports that are incompatible with webpack in the main applications: import { ArrowDropDownCircleOutlined } from &a ...

Utilize puppeteer and web-vitals in NextJS to retrieve the web performance metrics of a website

I'm currently working on a basic tool in NextJS that uses puppeteer to fetch web vitals data from a given URL. However, I'm facing an issue where the results are not being printed out. What could be causing this problem? const browser = await pup ...

Exploring the world of mouse events in Typescript using JQuery, all the while maintaining strict typing regulations

I'm currently working on a project that involves using JQuery in Typescript. One challenge I'm facing is passing a mouse event from a JQuery element to a wrapper class. Here's an example of what I'm trying to achieve: import * as $ fro ...

What is the best way to extract a specific property from an object?

Consider the following function: getNewColor(): {} { return colors.red; } Along with this object: colors: any = { red: {primary: '#ad2121',secondary: '#FAE3E3'}, blue: {primary: '#1e90ff',secondary: '#D1E8FF&apos ...