Determining the object's type based on a property value in TypeScript

Below is my simplified TypeScript code snippet:

interface TimeoutOption {
  category: 'timeout'
  time: number
}

interface UserOption {
  category: Parameters<typeof addEventListener>[0]
}

type MyDelayOptions = Array<TimeoutOption | UserOption>


function delay(options: MyDelayOptions) {
  for (const option of options) {
    if (option.category === 'timeout') {
      timeout = setTimeout(() => {
        // not relevant
      }, option.time) // need to cast and do (option as TimeoutOption) to not get an error
    }
  }
}


To prevent a compilation error, I am currently using the type assertion mentioned in the comment. However, it should be clear to humans that if the category is 'timeout', then the option is of type TimeoutOption. How can I modify this without the need for a type assertion? Any suggestions for complete refactorings are appreciated.

Answer №1

The issue lies in the fact that UserOption defines the property category as type string, which indirectly makes it conflicting. Therefore, when checking

if (option.category === 'timeout')
, it cannot distinguish between the two union members because both UserOption and TimeoutOption can have category: "timeout". The discriminant (category) must clearly differentiate the union types, but in this case, it does not.

An alternative approach could be to check for the existence of time instead (refer to ***):

function delay(options: MyDelayOptions) {
  for (const option of options) {
    if ("time" in option) { // ***
      const timeout = setTimeout(() => {
        // implementation not shown
      }, option.time); // casting and using (option as TimeoutOption) is necessary to prevent errors
    }
  }
}

Link to Playground

Answer №2

Seems like a typeguard function is necessary in this situation to validate that option belongs to the type of TimeoutOption.

Check out this link for more information on advanced types in TypeScript.

You can use something similar to the following code snippet for this purpose:

function isTimeout(option: TimeoutOption | UserOption): option is TimeoutOption {
  return (option as TimeoutOption).time !== undefined;
}

...

if (itTimeout(option) {
 // do stuff
}

You can experiment with the code by clicking here.

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

Encountering an ERROR of TypeError when attempting to access the property 'length'

I encountered the following error message: ERROR TypeError: Cannot read property 'length' of undefined at eval (webpack-internal:///./node_modules/@angular/common/esm5/http.js:163) at Array.forEach () at HttpHeaders.lazyInit ...

TypeOrm is struggling to identify the entities based on the directory path provided

Currently, I am utilizing TypeORM with NestJS and PostgreSql to load entities via the datasource options object. This object is passed in the useFactory async function within the TypeORM module as shown below: @Module({ imports: [TypeOrmModule.forRootAsy ...

What is the process for ensuring that the "ng-multiselect-dropdown" is a mandatory field within Angular 7?

Is there a way to require the ng-multiselect-dropdown field to have at least one selected item? <ng-multiselect-dropdown [placeholder]="'Select countries'" [data]="countries" [(ngModel)]="countriesSelectedItems" [settings]="co ...

Ways to ensure that your Angular component.ts file is executed only after the body has completely loaded without relying on any external

I am attempting to add an event listener to an element created with a unique identifier using uuid, but I keep getting an error that states "Cannot read properties of null (reading 'addEventListener')" export class CommentItemComponent implements ...

Exploring the capabilities of UIGrid in conjunction with TypeScript DefinitelyTyped has been a

I've noticed that the latest release of UI Grid (RC3) has undergone significant architectural changes compared to nggrid. I am encountering some problems with the definitelytyped files for nggrid because they are from a different version. Will there ...

A call signature is missing in the expression type of Typescript, preventing it from being invoked

While I know there are similar questions out there, none of them have provided the answer I'm looking for. My goal is to create a straightforward function in my Angular application. In my app.component.ts file: formClick() { const formContainer ...

What could be causing TypeScript to not locate my custom package?

I decided to create a fork of an existing package and released it with a new, distinct name: https://www.npmjs.com/package/feed-media-fork After tagging a new version, setting up a release on GitHub, and running yarn add feed-media-fork or the equivalent ...

Problem encountered while directing to a component within Angular

Here is the overview of my directory structure: Directory Structure login.component.ts: import { Component, OnInit } from '@angular/core'; import { FormBuilder, FormControl, FormGroup, Validators, ReactiveFormsModule } from '@angular/forms ...

Specifying data types for keys within a generic parameter that is inferred

Objective I am currently developing a utility with the following interface: interface F<M extends Record<string, Function>> { ( listeners: M, options: { filter: e => boolean } ): void } An important aspect here is the filter o ...

Removing special characters when pasting into text box in Angular 2 or higher versions

To ensure that special characters are trimmed or removed when pasting into a textbox inside the TypeScript component file (with extension .ts), utilize a function within the TypeScript component itself. The modified text should be displayed in the textbox ...

There was an issue: Control with the name 'name' could not be located

Whenever I submit the form and try to go back, an error pops up saying "ERROR Error: Cannot find control with the name: 'name'". I'm not sure what I might be missing. Do I need to include additional checks? Below is my HTML file: <div ...

Bringing in External Components/Functions using Webpack Module Federation

Currently, we are experimenting with the react webpack module federation for a proof of concept project. However, we have encountered an error when utilizing tsx files instead of js files as shown in the examples provided by the module federation team. We ...

Angular date selection with a range of plus two days, factoring in the exclusion of weekends

I am currently using a mat date picker range with specific logic. The minimum date that a user can select on the calendar is set to + 2 days. For example, if today's date is July 20, 2022, the minimum selectable date would be July 22, 2022. However, ...

Leveraging Classes for Http Observables in Angular 12

Utilizing the power of Angular 12 The backend response received from the HTTP service is structured as follows: Array<{ id: string; title: string; created: string; }> // Service public list(): Observable<Array<Item>> { return ...

What is the best way to create a TypeScript function that merges actions together?

I am currently working on a TypeScript function similar to the following: import multipleActionObject from page; it("should be able to perform multiple operations", () => { multipleActionObject.chooseIndex(4).setValue(10); } Inste ...

Retrieve the non-empty attributes of a JSON object

I have a function in Typescript that extracts specific values from a JSON data object, some of which may be empty. How can I retrieve only certain data fields? Here is the function: let datosCod; for (let get in Object.keys(transfConsData)) { co ...

Tips for specifying a custom type as the return value of a function and denote it as a promise

I encountered this code snippet: import * as Promise from 'bluebird'; import {Response} from '../commonInterfaces/httpService'; getCall(url: string, accessToken: string, extraHeaders: object): Promise<Response> { let headers ...

Pull out a collection from an entity

I'm struggling with retrieving an array from within an object. The structure is shown below: [] 0: Array(4) 0: 0 1: 5 2: 500 3: (5) [{…}, {…}, {…}, {…}, {…}] Despite attempting methods like using object.keys, for loop ...

Avoid the need for props when implementing a component with a higher order component

Running into an issue with HOC and typescript. The compiler is asking for a value that is received from the HOC. Here's the component using the HOC: function Coupon(props: WithAlertProps): JSX.Element { return <p>test {props.error}</p> } ...

What led the Typescript Team to decide against making === the default option?

Given that Typescript is known for its type safety, it can seem odd that the == operator still exists. Is there a specific rationale behind this decision? ...