Filter array of objects by optional properties using TypeGuards

In my TypeScript code, I have defined the following interfaces:

interface ComplexRating {
  ratingAttribute1?: number;
  ratingAttribute2?: number;
  ratingAttribute3?: number;
  ratingAttribute4?: number;
}

export interface Review {
  rating: ComplexRating | number;
}

My goal is to calculate an average rating for the ratingAttribute1 attribute. For example, given these reviews:

const reviews: Review[] = [
  { rating: { ratingAttribute1: 5 } },
  { rating: { ratingAttribute1: 10 } },
  { rating: { ratingAttribute2: 15 } },
  { rating: 5 }
]

I want to filter the reviews to focus only on the ones with ratingAttribute1. This is the function I have created for calculating the average rating:

const calculateAverageRating = (reviews: Review[]): number => {
  const reviewsWithRating = reviews.filter(
    (review) =>
      typeof review.rating === 'object' &&
      typeof review.rating['ratingAttribute1'] === 'number'
  );
  return (
    reviewsWithRating.reduce((acc, review) => {
      let newValue = acc;
      if (typeof review.rating === 'object') {
        const rating = review.rating['ratingAttribute1'];
        if (rating) {
          newValue += rating;
        }
      }
      return newValue;
    }, 0.0) / reviewsWithRating.length
  );
};

However, TypeScript does not recognize the type guarding done by the reviews.filter function, which can be seen in this screenshot:

Is there a way to improve this type guarding and eliminate the need for repeating type checks in the calculation function?

Answer №1

The function .filter() always returns an array in the same format as the one provided as the argument. This is why, even after filtering, reviewsWithRating remains a Review[].

To modify this behavior, you can implement a type guard in the callback function:

const reviewsWithRating = reviews.filter(
  (review): review is { rating: Required<ComplexRating> } =>
    typeof review.rating === 'object' &&
    typeof review.rating['ratingAttribute1'] === 'number'
);

By doing this, TypeScript will now recognize that reviewsWithRating is a type of

{ rating: Required<ComplexRating> }[]
.

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

Trouble arises when trying to open a new window using the Angular 8 CDK

I am attempting to open my component in a new window, similar to this example: https://stackblitz.com/edit/angular-open-window However, when the window opens, my component is not displayed and I receive the following error in the console: Error: Must pro ...

"NgFor can only bind to Array objects - troubleshoot and resolve this error

Recently, I've encountered a perplexing error that has left me stumped. Can anyone offer guidance on how to resolve this issue? ERROR Error: Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supp ...

Convert a PHP multi-dimensional array into an object structure using recursion

I am struggling to convert a multidimensional array into an object in order to output it as both XML and JSON. Despite researching numerous posts on the topic, I still can't wrap my head around how to do this recursively. What mistake am I making? c ...

Trouble with Angular2: Socket.io event not refreshing the view

I attempted to update my view upon receiving a socket event. This is what my component code looks like: constructor(private _zone: NgZone){ this.socket = io.connect('http://localhost:3000'); this.socket.on('someEvent', function ...

What is the method for adjusting the spacing between binding tags within HTML code formatting specifically for TypeScript elements in IntelliJ?

My Angular binding currently defaults to <h1>{{typeScriptVar}}</h1>, but I would like it to be set as <h1>{{ typeScriptVar }}</h1> when I use the format code shortcut in InteliJ. Can anyone assist me with this issue? I have resear ...

Issue: Unable to resolve all parameters for LoginComponent while implementing Nebular OAuth2Description: An error has occurred when attempting to

I have been attempting to utilize nebular oauth in my login component, following the documentation closely. The only difference is that I am extending the nebular login component. However, when implementing this code, I encounter an error. export class Lo ...

What is the equivalent of Typescript's Uint8Array and Uint16Array in Python?

new Uint8Array(new Uint16Array([64]).buffer) How can I achieve a similar data structure in pure Python? What is the equivalent of Uint8Array/Uint16Array? I am extracting a buffer from a Uint16Array type here and converting it to a Uint8Array, but I am un ...

In TypeScript, what specific type or class does a dynamically imported module belong to?

Can someone assist me in determining the type of an imported module in TypeScript? Here is my query: I have a module called module.ts export class RSL1 {}; Next, I import it into my index.ts using the following code: const script = await import('mod ...

Is there a way to access the initial element of the array within this variable assignment?

When utilizing the element id 10_food_select: var id = $(sel).attr("id").split("_"); An array is generated as follows: ["10", "food", "select"] The desired outcome is to have id = 10 (or whichever value is in the first element). This can be achieved l ...

The data type does not match the expected type 'GetVerificationKey' in the context of express-jwt when using auth0

I am in the process of implementing auth0 as described here, using a combination of express-jwt and jwks-rsa. However, I encountered an error like the one below and it's causing issues with finishing tsc properly. Error:(102, 5) TS2322: Type 'S ...

Building a continuous timer loop in Angular using RxJS that adapts to changing durations within an array's objects

I am experimenting with a scenario where I read the data, loop based on the duration. For example, starting with "Adam" first, play Adam for a 15-second timer, then move on to the next beginner "Andy" and play Andy for 15 seconds. Once we reach group "int ...

While working on my Python currency converter project, I encountered an issue where the program returned an error stating that the variable 'money' was not defined, despite previously defining it. This problem arose specifically during

usd = 2 yen = 1000 gbp = 1.7 eur = 0.75 def again(): selection = False while selection == False: amount = input("Please enter an amount ") try: int(amount) except ValueError: print("Invalid: Please ...

Setting a default value for a data type within Typescript

My goal is to set default values for all properties in my custom type if they are not defined. This is what I have done: // custom type with optional properties type MyType = { // an example property: str?: string } // with numerous properties, assign ...

AngularJS allows you to hide an accordion if its sub-elements are filtered out

I have dynamically created an accordion using Angular. Here is the code snippet: <input type="text" class="form-control pull-left" ng-model="searchSingleField.title"> <accordion-group ng-repeat="category in categories"> <li ng-repeat ...

Fetching all data from a SQLite database in a Listview using React Native

I have been utilizing the library found at https://github.com/andpor/react-native-sqlite-storage in my react native project. To retrieve a single row from the database, I use the following code: db.transaction((tx) => { tx.executeSql('SEL ...

How to use Selenium to interact with an array of string elements and perform clicks

I have a String array that I obtain from either a text file or CSV. My goal is to use Selenium to click on each checkbox element corresponding to the elements in the array. The checkboxes follow this naming convention: 1.0.50.gecb16, 1.1.50.gecb16, 1.2. ...

Changing the Structure of an Array

I am working on maintaining the structure of my array as shown below: array ( 0 => array ( "url"=> "http://localhost/theme/wp-content/uploads/2017/08/img2.png" "caption"=> "title text test" ) ) However, currently my array i ...

Utilizing Typescript Generics in Arrow Function to Combine Two Arguments

For instance, I am working with this code in a .tsx file extension const Add = <T,>(arg0: T, arg1: T): T => arg0 + arg1; const A = Add(1, 2); const B = Add('1', '2') However, I am encountering an issue, as there is an error m ...

Issues with hydrating React local storage hook in custom implementation within NextJS

Currently facing an issue while implementing the localstorage hook in NextJS. The error message I am encountering is: Error: Hydration failed because the initial UI does not match what was rendered on the server.. Any suggestions on what might be causing ...

Interface of TypeScript Undetermined

Currently, I am developing a Demo API Wrapper specifically for Roblox. During the development process, I have come across a certain issue that I would like to address. My aim is to send a request and then return all the data in the manner of an API wrapper ...