Creating composite type guards - passing down properties from the type guard to the calling function

type Bird = { fly: () => "fly" };
type Insect = { annoy: () => "annoy" };
type Dog = { beTheBest: () => "dogdogdog" };

type Animal = Bird | Insect | Dog;

const isBird = (animal: Animal): animal is Bird => {
  if ("fly" in animal) return true;
  return false;
};

const isInsect = (animal: Animal): animal is Insect => {
  if ("annoy" in animal) return true;
  return false;
};

const hasWings = (animal: Animal) => {
  if (isBird(animal)) return true;
  if (isInsect(animal)) return true;
  return false;
};

The author is trying to create a composite type-guarding function named hasWings by combining the existing isBird and isInsect guards. However, TypeScript fails to infer that hasWings is also acting as a type guard. The author is exploring ways to explicitly specify this without manually restating the criteria each time.

// One suggestion is to define the return type explicitly:
const hasWings = (animal: Animal): ReturnType<typeof isBird> & ReturnType<typeof isInsect> => {
  if (isBird(animal)) return true;
  if (isInsect(animal)) return true;
  return false;
};

// Another approach is to manually list out the criteria:
const hasWings = (animal: Animal): animal is Insect | Bird => {
  if (isBird(animal)) return true;
  if (isInsect(animal)) return true;
  return false;
};

Answer №1

If you want to streamline the process of combining type-guards, creating a helper function might be the way to go. Even though the type annotations may seem complicated, here's how you can do it:

type TypeGuard<S, T extends S> = (x: S) => x is T;

function typeGuardUnion<T extends TypeGuard<any, any>[]>(...fs: T): TypeGuard<
    T extends TypeGuard<infer S, any>[] ? S : never,
    T extends TypeGuard<any, infer U>[] ? U : never
> {
    return ((x: any) => fs.some(f => f(x))) as any;
}

// TypeGuard<Animal, Bird | Insect>
const hasWings = typeGuardUnion(isBird, isInsect);

Playground Link

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 causes an array to accumulate duplicate objects when they are added in a loop?

I am currently developing a calendar application using ExpressJS and TypeScript. Within this project, I have implemented a function that manages recurring events and returns an array of events for a specific month upon request. let response: TEventResponse ...

Issue TS1192: The module named "A.module" does not contain a default export

After creating a new module 'A', I attempted to import it in another module named 'B'. However, during compilation, I encountered the following error: Error TS1192: Module '" A.module"' has no default export I wou ...

After selecting an item, the Next UI navbar menu seems to have trouble closing

Having trouble with the navbar menu component not closing when an option is selected from the menu. The menu does open and close successfully within the menu icon. I attempted to use onPress() but it doesn't seem to be working as expected. "use c ...

Determining the parent type in Typescript by inferring it from a nested member

Typescript has the ability to infer the type of a value based on queries made within if statements. For instance, the type of one member of an object can be deduced based on another: type ChildType = 'a' | 'b'; type Child<T extends ...

Arranging Angular Cards alphabetically by First Name and Last Name

I am working with a set of 6 cards that contain basic user information such as first name, last name, and email. On the Users Details Page, I need to implement a dropdown menu with two sorting options: one for sorting by first name and another for sorting ...

Prevent click events on disabled tabs in PrimeNG tabMenu

I am encountering an issue with PrimeNG's p-tabMenu when it comes to disabled menu items. For example, suppose I have a tabMenu with 4 sub tabs labeled AAA, BBB, CCC, and DDD. This is how the menuItems are set up in the TypeScript component: //.... ...

What could be causing the QullJS delta to display in a nonsensical sequence?

The outcome showcased in the delta appears as: {"ops":[{"retain":710},{"insert":" yesterday, and she says—”\n“The clinic?","attributes":{"prediction":"prediction"}},{"del ...

Incorporate MUX Player (Video) into Angular versions 14 or 15

Mux offers a video API service with its own player: MUX Player I am interested in integrating this npm package specifically into a component in Angular 14/15. The JavaScript should only be loaded when this particular component is rendered. Integration Th ...

Is there a method to make this package compatible with Angular version 16?

I recently integrated the ngx-hotjar package version 11.0.0 into my Angular 10 project with success. However, when trying to use it in a new Angular 16 project, I encountered the following error during ng serve: Error: src/app/app.module.ts:275:12 - error ...

Avoid unnecessary re-rendering of React Native components with each update of the state

I need my component to display either A or B based on the user's proximity to a specific location. I developed a custom hook to determine if the user is nearby. However, I'm facing an issue where the hook constantly returns a new value of true, ...

What is the best way to format a string into a specific pattern within an Angular application

In my Angular component, I have 4 fields: customerName, startDate, and startTime. Additionally, I have a fourth field that is a textarea where the user can see the message that will be sent via email or SMS. Within my component, I have defined a string as ...

Using Angular Ngrx to Retrieve and Showcase a Selection of Choices from a Java API

When accessing the Java API at localhost://company/products/123/fetchOptions, you can expect a response similar to the following: { "Increase": true, "Decrease" : true, "Like" : true, "Dislike" : true, "Old" : false, "Others" : true } Using Angula ...

Angular - Using HttpClient for handling POST requests

The example provided in the official Angular HttpClient documentation demonstrates how to make a POST request to a backend server. /** POST: add a new hero to the database */ addHero (hero: Hero): Observable<Hero> { return this.http.post<Hero&g ...

Troubleshoot: Angular5 Service call not functioning properly when called in ngOnInit

Every time I go to the results component, the service inside ngOnInit behaves as expected. However, when I open the side menu, navigate to another page, and then return to the results page, the page fails to render the results. Instead, the ng-template is ...

Combining URL parameters into an object with NestJS's HTTP module

Is there a method for passing URL parameters to the httpService in nestjs? I want to improve the readability of this URL without resorting to Axios as nest has its own HTTPModule. Currently, this is how it looks and functions: const response = await this. ...

Verify whether the default export of a file is a React function component or a standard function

Trying to figure out how to distinguish between modules exporting React function components and regular functions. Bun employs file-based routing, enabling me to match requests with module files to dynamically import based on the request pathname. Conside ...

Learn the process of transferring a complete JSON object into another object using Angular

I'm having trouble inserting one object into another. My JSON object is multidimensional, like this: { "item1":{ "key":"Value", "key":"Value" }, "item2":{ "key ...

The letter 'X' is not suitable for use as a JSX component because its return type 'Element[]' does not qualify as a valid JSX element

Currently, I am working on rendering two simple joke cards in TypeScript. The cards are displaying correctly in my browser, but I've encountered an error message that says: 'Jokes' cannot be used as a JSX component. Its return type 'Ele ...

Can TypeScript restrict a callback parameter based on the type of another parameter using generics?

I am currently developing an event manager system. The main objective is to allow users to subscribe to events by providing an event type and a callback function. In my implementation, events are represented as classes, where AwesomeEventType in the exampl ...

Extending injections in Angular 5 by inheriting from a base class

I have created a class with the following structure: @Injectable FooService { constructor(protected _bar:BarService){ } } Then, I extended this class as shown below: @Injectable ExtFooService extends FooService { constructor(_bar:BarServi ...