Identify a singular instance of a union in Typescript without prejudice

Can options of a union be differentiated one by one, and if no case matches a discriminated interface, can it fallback to another interface?

enum ActionType {
    add = 'add',
    remove = 'remove',
    modify = 'modify',
}

interface ActionLogDefault {
    action: ActionType;
    itemId: number | string | undefined // generic interface, all types included
    itemType: string;
}

interface ActionLogAdd {
    action: ActionType.add
    itemId: undefined;
}

type ActionLogOptions = ActionLogDefault | ActionLogAdd

declare const log: ActionLogOptions

if (log.action === ActionType.add) {
    log.itemId // should be undefined
}

Answer №1

Utilizing conditional generic types can help to differentiate them instead

enum ActionType {
    create = 'create',
    delete = 'delete',
    update = 'update',
}

interface ActionHistoryDefault {
    type: ActionType;
    entityId: number | string | undefined 
    entityType: string;
}

interface ActionHistoryInsert extends ActionHistoryDefault {
    type: ActionType.create
    entityId: undefined;
}

type ActionHistoryOptions<T extends ActionType = any> = T extends ActionType.create ? ActionHistoryInsert : ActionHistoryDefault

declare const a: ActionHistoryOptions<ActionType.create>
a.entityId // undefined

declare const b: ActionHistoryOptions
b.entityId // number | string | undefined 

If you do not specify anything to the generic types of ActionHistoryOptions, it will default to ActionHistoryDefault.

Playground


Thanks for @PiotrSzyma suggestion in the comment section.

Rather than utilizing a common type, consider having separate types for various actions. This way, your type checks will be accurately applied within if-statements

enum ActionType {
    create = 'create',
    delete = 'delete',
    update = 'update',
}

interface ActionHistoryDelete {
    type: ActionType.delete;
    entityId: number | string | undefined
    entityType: string;
}

interface ActionHistoryUpdate {
    type: ActionType.update;
    entityId: string;
}

interface ActionHistoryInsert {
    type: ActionType.create
    entityId: undefined;
}

type ActionHistory = ActionHistoryInsert | ActionHistoryUpdate | ActionHistoryDelete;

declare const a: ActionHistory

if (a.type === ActionType.create) {
    a.entityId // undefined
}

if (a.type === ActionType.update) {
    a.entityId // should be string
}

if (a.type === ActionType.delete) {
    a.entityId // number | string | undefined
}

Playground

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

Error: The function list.forEach does not exist within service.buildList [as project]

Today, I've been grappling with a challenging issue. As someone new to Typescript and Angular, I'm attempting to make a call to my backend API. However, when trying to populate an array for display, I keep encountering an error that says rawRegis ...

What is the best way to assign a value to an option element for ordering purposes?

My select element is being populated with fruits from a database, using the following code: <select name="fruitsOption" id="fruitsOptionId" ngModel #fruitRef="ngModel"> <option *ngFor="let fruit of fruits">{{fruit}}</option> </selec ...

Having trouble implementing object type switching in Typescript

While in the process of developing an angular application, I stumbled upon a peculiar issue. Some time ago, I crafted this piece of code which performed flawlessly: selectedGeoArea: any receiveStoreEvent(event) { switch (event.constructor) { ca ...

How can we bring in a JavaScript file to an Angular 2 project?

I've been struggling with importing a JavaScript file into my Angular2 project. This particular file is not a module from npm, and the usual instructions using npm don't apply in this case. My setup involves using Angular CLI, and within my angu ...

Monitoring modifications in elements within an array using Angular2

Currently using Angular 2 and typescript, I have an array in which I am utilizing DoCheck and IterableDiffer to monitor any changes. While I receive notifications when the array itself is modified, I do not get notified if a property within one of the obje ...

Passing a reference to a react functional component (react.FC) results in a type error: The property ref is not recognized on the type 'IntrinsicAttributes & Props & { children: ReactNode}'

Currently, I am working on mastering the utilization of forward refs. In a functional component (FC), I am trying to initialize all my refs and then pass them down to its child components so that I can access the canvas instances of some chartjs charts. Ho ...

Step-by-step guide on incorporating an external JavaScript library into an Ionic 3 TypeScript project

As part of a project, I am tasked with creating a custom thermostat app. While I initially wanted to use Ionic for this task, I encountered some difficulty in integrating the provided API into my project. The API.js file contains all the necessary function ...

Definition files (.d.ts) for JavaScript modules

I'm currently working on creating Typescript typings for the link2aws package in order to incorporate it into my Angular project. Despite generating a .d.ts file, I am still encountering the following error message: TypeError: (new link2aws__WEBPACK_I ...

Using Angular with THREE JS integration in Javascript

I am currently experimenting with Angular and facing a challenge that I can't seem to figure out. The issue I am encountering involves integrating a javascript code, SunLight.js, from the repository https://github.com/antarktikali/threejs-sunlight in ...

Error: Oops! The super expression can't be anything other than null or a function in JavaScript/TypeScript

I am facing an issue with class inheritance in my code. I have a class A that extends class B, which in turn extends class C. Whenever I try to create a new instance of class A within a function, I encounter the following error message: Uncaught TypeError: ...

Difficulty in monitoring the present machine status through XState in a React application

I'm encountering an issue where I am trying to access the Machine state from within a function in a React component using state.value. However, the current state never changes and it always displays the initial state. Strangely, if I include an onClic ...

Utilize an array as the response model in Amazon API Gateway using the AWS CDK

I am currently in the process of developing a TypeScript AWS CDK to set up an API Gateway along with its own Swagger documentation. One of the requirements is to create a simple endpoint that returns a list of "Supplier", but I am facing challenges in spec ...

The issue of Angular 9 not recognizing methods within Materialize CSS modals

I am currently working on an Angular 9 application and have integrated the materialize-css 1.0 library to incorporate a modal within my project. Everything works smoothly in terms of opening and instantiating the modal. However, I have encountered an issue ...

Apologies, the module "@org-name/package-name" could not be located

I've hit a roadblock for the past few days. My goal is to create a new npm package that wraps an API I've been developing. When bundling the package, everything seems to be fine in the /dist folder. However, when attempting to test it in a front ...

Having trouble importing zone.js in Angular 14 and Jest 28

I am currently in the process of updating to Angular 14. Everything is going smoothly except for setting up jest. Since I have Angular 14 libraries included in my build, I need to utilize jest-ESM support. Below is my configuration: package.json { &qu ...

Error: Trying to access a property that does not exist on an undefined object (retrieving 'kind

Currently, I am working on a project using angular-CLI. When I attempted to create a new module yesterday, an error popped up in the terminal saying Cannot read properties of undefined (reading 'kind') (only this error there wasn't an ...

Tips for effectively sending prop to a component in React with the help of TypeScript

Hey there, I'm working on a component called FormField which can accept either an icon for create or edit. Currently, I am using this FormField inside another component called SelectWithFormField. Here's how it looks: const FormField = ({create, ...

Unable to refresh the context following a successful API call

My current project in NextJS requires a simple login function, and I have been attempting to implement it using the Context API to store user data. However, I am facing an issue where the context is not updating properly after fetching data from the back-e ...

Is there any advice for resolving the issue "In order to execute the serve command, you must be in an Angular project, but the project definition cannot be located"?

Are there any suggestions for resolving the error message "The serve command requires to be run in an Angular project, but a project definition could not be found."? PS C:\angular\pro\toitsu-view> ng serve The serve command requires to be ...

Identifying the specific @Input that has changed in ngOnChanges

I am currently utilizing Angular 2. At the moment, I have two @input variables named aa and bb. My objective is: When aa changes, perform a specific action. When bb changes, execute a different action. How can I identify which @Input has changed within ...