Original: Generic for type guard functionRewritten: Universal

I have successfully created a function that filters a list of two types into two separate lists of unique type using hardcoded types:

interface TypeA {
  kind: 'typeA';
}
interface TypeB {
  kind: 'typeB';
}
filterMixedList(mixedList$: Observable<(TypeA | TypeB)[]>): Observable<TypeA[]> {
  return mixedList$.pipe(
    map(items => items
      .filter((item): item is TypeA => item.kind === 'typeA')),
  );
}

Is there a way to create a generic solution to avoid hardcoding the types? Any help would be greatly appreciated.

PS: You can find a minimal reproducible example here: stackblitz

Answer №1

To create a more versatile solution, you can pass the type guard function as a parameter. Here's how it would look:

function getTypeFromMixedList<T, U extends T>(
  mixedList$: Observable<T[]>,
  guard: (item: T) => item is U
): Observable<U[]> {
  return mixedList$.pipe(map(items => items.filter(guard)));
}

If you're working with a discriminated union type, you can also provide the discriminant key and value to check for:

function getTypeFromDiscriminatedUnionList<
  T,
  K extends keyof T,
  V extends (T[K]) & (string | number | undefined | null)
>(
  mixedList$: Observable<T[]>,
  key: K,
  val: V
): Observable<Extract<T, { [P in K]?: V }>[]> {
  return mixedList$.pipe(
    map(items =>
      items.filter(
        (item): item is Extract<T, { [P in K]?: V }> => item[key] === val
      )
    )
  );
}

You can use either approach in your specific scenario like this:

getTypeFromMixedList(of([a, b]), (x): x is TypeA => x.kind === "typeA").subscribe(
  v => (dataDivA.innerHTML = JSON.stringify(v))
);

getTypeFromDiscriminatedUnionList(of([a, b]), "kind", "typeB").subscribe(
  v => (dataDivB.innerHTML = JSON.stringify(v))
);

I hope this information proves helpful. Best of luck with your implementation!

Here is the Stackblitz link to the code

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

Issue with my TypeScript modules TS2307: Module not found

For my latest project, I decided to use the aurelia-typescript-skeleton as the foundation. To enhance it, I created a new file called hello.ts in the src folder. export class Hello { sayHello(name:string) : string { return 'Hello ' + name; ...

Accessing the currently operating WS server instance with NodeJS

After successfully setting up a basic REST API using NodeJS, ExpressJS, and routing-controllers, I also managed to configure a WebSocket server alongside the REST API by implementing WS. const app = express(); app.use(bodyParser.json({limit: "50mb"})); a ...

The specified class is not found in the type 'ILineOptions' for fabricjs

Attempting to incorporate the solution provided in this answer for typescript, , regarding creating a Line. The code snippet from the answer includes the following options: var line = new fabric.Line(points, { strokeWidth: 2, fill: '#999999', ...

Implementing context menus on the Material-UI DataGrid is a straightforward process that can enhance the user experience

I am looking to enhance my context menus to be more like what is demonstrated here: Currently, I have only been able to achieve something similar to this example: https://codesandbox.io/s/xenodochial-snow-pz1fr?file=/src/DataGridTest.tsx The contextmenu ...

The ultimate guide to loading multiple YAML files simultaneously in JavaScript

A Ruby script was created to split a large YAML file named travel.yaml, which includes a list of country keys and information, into individual files for each country. data = YAML.load(File.read('./src/constants/travel.yaml')) data.fetch('co ...

Change TypeScript React calculator button to a different type

I am currently troubleshooting my TypeScript conversion for this calculator application. I defined a type called ButtonProps, but I am uncertain about setting the handleClick or children to anything other than 'any'. Furthermore, ...

How can we avoid excessive re-rendering of a child component in React when making changes to the parent's state?

In my React application, I am facing a situation where a parent component controls a state variable and sends it to a child component. The child component utilizes this state in its useEffect hook and at times modifies the parent's state. As a result, ...

I am attempting to send an array as parameters in an httpservice request, but the parameters are being evaluated as an empty array

Trying to upload multiple images involves converting the image into a base64 encoded string and storing its metadata with an array. The reference to the image path is stored in the database, so the functionality is written in the backend for insertion. Ho ...

Looking for a more efficient approach to writing React styles for color?

Desire I am interested in customizing colors within Material UI components. Moreover, I aim to develop a React Component that allows for dynamic color switching through Props. Challenge The current approach using withStyles results in redundant and lengt ...

Creating a gradient background with the makeStyles function

Why is it that the background: 'linear-gradient(to right, blue.200, blue.700)' doesn't work within makeStyles? I just want to add a gradient background to the entire area. Do you think <Container className={classes.root}> should be rep ...

Derive the property type based on the type of another property in TypeScript

interface customFeatureType<Properties=any, State=any> { defaultState: State; properties: Properties; analyzeState: (properties: Properties, state: State) => any; } const customFeatureComponent: customFeatureType = { defaultState: { lastN ...

Constructing a hierarchical tree structure using an array of objects that are initially flat

My goal is to create a hierarchical tree structure from a flat array: The original flat array looks like this: nodes = [ {id: 1, pid: 0, name: "kpittu"}, {id: 2, pid: 0, name: "news"}, {id: 3, pid: 0, name: "menu"}, {id: 4, pid: 3, name: ...

The concept of Typescript involves taking a particular type and generating a union type within a generic interface

Picture a straightforward CollectionStore that contains methods for creating and updating a record. The create() method takes in a set of attributes and returns the same set with an added id property. On the other hand, the update method requires the set t ...

Tips for declaring a particular type during the process of destructuring in Typescript

I have my own custom types defined as shown below: export type Extensions = | '.gif' | '.png' | '.jpeg' | '.jpg' | '.svg' | '.txt' | '.jpg' | '.csv' | '. ...

Issues with identifying the signature of a class decorator arise when invoked as an expression

I've been following this coding example but I'm running into issues when trying to compile it. Any suggestions on how to troubleshoot this error? import { Component } from '@angular/core'; function log(className) { console.log(class ...

What is the method for assigning a string to module variable definitions?

As someone new to TypeScript and MVC, I find myself unsure if I am even asking the right questions. I have multiple TypeScript files with identical functionality that are used across various search screens. My goal is to consolidate these into a single fil ...

Exploring the best way to access ViewContainerRef: ViewChild vs Directive

While researching, I came across a recommendation in the Angular Docs that suggests using a directive to access the ViewContainerRef for creating dynamic components. Here is an example of such a directive: import { Directive, ViewContainerRef } from &apos ...

Setting up Emotion js in a React TypeScript project using Vite 4

Currently, I am in the process of transitioning from Webpack to Vite for my React Typescript application. I have been attempting to integrate Emotion js into the project. "@vitejs/plugin-react": "^4.0.1", "vite": "^4.3.9 ...

I am interested in creating a class that will produce functions as its instances

Looking to create a TypeScript class with instances that act as functions? More specifically, each function in the class should return an HTMLelement. Here's an example of what I'm aiming for: function generateDiv() { const div = document.crea ...

Excluding certain source files in Typescript's tsconfig is a common practice to

My attempt to configure Typescript to exclude specific files during compilation is not working as expected. Despite setting exclusions in my tsconfig.json file, the code from one of the excluded files (subClassA.ts) is still being included in the compiled ...