Identifying the category of a value through a conditional check on another value

I am looking for my code editor to automatically determine the type of extraData based on the value of error, which is being narrowed down by an if statement:

export enum ErrorCodes {
  Unknown = 'UNKWN',
  BadRequest = 'BDREQ',
}

interface ExtraData {
  [ErrorCodes.Unknown]: string,
  [ErrorCodes.BadRequest]: number,
}

let error: ErrorCodes;
let extraData: ExtraData[typeof error];

if(error === ErrorCodes.Unknown){
  console.log(extraData) // VS code indicates it as a string | number when hovered over
}

However, the issue arises when trying to assign the type of extraData inside the if block:

export enum ErrorCodes {
  Unknown = 'UNKWN',
  BadRequest = 'BDREQ',
}

interface ExtraData {
  [ErrorCodes.Unknown]: string,
  [ErrorCodes.BadRequest]: number,
}

let error: ErrorCodes;

if(error === ErrorCodes.Unknown){
  let extraData: ExtraData[typeof error];
  extraData // VS code shows it as a string when hovering over
}

The challenge is to avoid repeating this pattern in every conditional block because there are numerous error codes, and I cannot use typeof since we will be using objects as actual extra data.

Answer №1

When error and extraData are simply two variables belonging to the types ErrorCodes and ExtraData[typeof error] respectively, a straightforward solution is not available. This is due to the fact that ErrorCodes is a union type, which results in ExtraData[typeof error] also being treated as a union type. In TypeScript, two union types are considered independent and unrelated entities. The specific type of typeof error remains strictly ErrorCodes, without any correlation to the actual value of error. Therefore, it is impossible to express a relationship between two separate variables of correlated union types.

If you wish to enable a mechanism where checking the value of error affects the inferred type of

extraData</code, both variables should be defined within a unified discriminated union type. Here’s an example illustrating how such a type can be defined:</p>
<pre><code>type ErrorAndExtra = { [K in keyof ExtraData]:
  { error: K, extraData: ExtraData[K] }
}[keyof ExtraData]

/* type ErrorAndExtra = {
    error: ErrorCodes.Unknown;
    extraData: string;
} | {
    error: ErrorCodes.BadRequest;
    extraData: number;
} */

The ErrorAndExtra type embodies a union structure where each member includes a distinct discriminant property error of literal type, along with an associated extraData property whose type depends on the value of the corresponding error property.

If a variable is declared to have the type ErrorAndExtra, the targeted narrowing behavior can be achieved as demonstrated below:

declare const e: ErrorAndExtra;
if (e.error === ErrorCodes.Unknown) {
  console.log(e.extraData.toUpperCase()) // valid
} else {
  console.log(e.extraData.toFixed()) // valid
}

This approach may meet your requirements effectively. Alternatively, the same outcome can be facilitated using separate variables by deconstructing them from a discriminated union type value:

declare const { error, extraData }: ErrorAndExtra;
if (error === ErrorCodes.Unknown) {
  console.log(extraData.toUpperCase()) // valid
} else {
  console.log(extraData.toFixed()) // valid
}

This method also works efficiently because TypeScript recognizes that the variables originate from the discriminated union type ErrorAndExtra, allowing the check performed on error to impact the apparent type of

extraData</code as intended.</p>
<p>It's important to note that this functionality is only feasible when the declarations of <code>error
and extraData conform to the supported model within TypeScript for this particular purpose. Deviations from this pattern could lead to issues, as demonstrated by the following scenario:

declare let { error, extraData }: ErrorAndExtra;
if (error === ErrorCodes.Unknown) {
  console.log(extraData.toUpperCase()) // error
} else {
  console.log(extraData.toFixed()) // error
}

In this case, changing the declaration from const to let results in a breakdown of the functionality. Since let variables allow reassignment, the compiler would need to monitor these assignments in order to determine if a check on

error</code should influence the observable type of <code>extraData
. However, this additional complexity is not deemed worthwhile for the compiler to handle.

Playground link to 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

I must address the drag-and-drop problem in reverse scenarios

I am currently utilizing react-dnd for drag and drop feature in my color-coding system. The implementation works flawlessly when I move a color forward, but encounters an issue when moving backward. Specifically, the problem arises when shifting a color ...

Nodejs and javascript are utilized to create dynamic SES emails

I have successfully implemented sending an email using node-ses client.sendEmail({ to: to_id, , cc: cc_id, , bcc: bcc_id, , subject: 'greetings' , message: 'your <b>message</b> goes here' , altText: 'plain text& ...

Looking for a way to scroll images without relying on the marquee tag? Whether you prefer using JavaScript, jQuery,

<marquee> <div class="client"> <img src="images/c1.png"/> </div> <div class="client"> <img src="images/c2.png"/> </div> <div class="client"> ...

Passing an array of integer arrays to a controller method in ASP MVC using Ajax

I am encountering an issue with my AJAX call when trying to post data entered by the user on the webpage to a controller method. Unfortunately, the data never reaches the controller and always results in an error. I suspect that the problem lies in the typ ...

What is the best way to show toast notifications for various selections in a dropdown menu?

I have a dropdown menu labeled "FavoriteFoods" that includes options such as "Pizza," "Sushi," "Burgers," and "Biryani." When one of these food choices is selected from the dropdown, I would like a toast pop-up to appear with the message "Great choice!" ...

I am encountering an issue where the POST data is not being successfully sent using XMLHttpRequest unless I include

I have a unique website where users can input a cost code, which is then submitted and POSTed to a page called 'process-cost-code.php'. This page performs basic validation checks and saves the information to a database if everything is correct. T ...

Iterating through a JSON object using an API loop

Just starting out in JS and I am attempting to use a for loop to combine all the 'text' from various JSON objects into one cohesive paragraph. For example, it should read like this: "Hello, and welcome to the Minute Physics tutorial on basic Rock ...

Retrieve records with at least one connection, but display all of them

After creating this entry, it now consists of 2 tags - tag1 and tag2. { "id": "d87de1d9-b048-4867-92fb-a84dca59c87e", "name": "Test Name", "tags": [ { "id": "fa0ca8fd-eff4-4e58-8bb0-a1ef726f01d4", "name": "tag1", "organizationI ...

Bringing in Static Functions Typescript

Having trouble importing a static class function into my main.js file and encountering an error after compiling with tsc: TypeError: Cannot read property 'router' of undefined at Object. (path/to/main.js:36:27)> Various attempts to assign a ...

"An issue with the colyseus server has been detected within the JavaScript code

I have written some code but it seems to be causing errors. const colyseus = require("colyseus"); const http = require("http"); const express = require("express"); const port = process.env.port || 3000; const app = express(); ...

What is the best way to include an item in a list with a specific identifier using the useState Record data type in a React application built with TypeScript?

Here is the structure of my Record type: const PEOPLE_MAP_INIT: Record<string, Person[]> = { "1": [], "2": [], "3": [] }; I have initialized the useState like this: const [PEOPLE_MAP, SET_PEO ...

Is there a way to view the text as I enter it while drawing text on an image canvas?

I currently have a canvas setup that allows users to upload an image and display it on the canvas. Users can then input text to be drawn on the image by clicking the submit button. However, I would like the entered text to appear on the image as it is bein ...

`Why am I having difficulty transmitting HTML content with Node.js through Mailgun?`

I've been facing issues with sending HTML in my emails. To troubleshoot and prevent errors, I've opted to utilize Mailgun's email templates. Although I can successfully send out the emails, the problem arises when I receive them - the HTML ...

Implementing a unique sorting algorithm for an array of numbers in Angular

I need to organize an array of numbers in descending order with a custom sorting method based on a specified number, all without splitting or filtering the array. I am currently working with Angular 17 and Rxjs 7.8. For instance, if I have this array of n ...

"Encountering a Heroku error due to an oversized cookie while using Express.js and

Being unsure of the exact cause, I am encountering an issue with Heroku that gives me the error message t=error code=H25 desc="HTTP restriction: oversized cookie" whenever I attempt to authenticate myself with Discord OAuth. Interestingly, this problem onl ...

I am puzzled as to why my text and div boxes are displaying in my navbar/hamburger menu instead of at the bottom of the page

Greetings, everyone! This is my debut post here so bear with me if it's not presented in the correct format. Currently, I am deep into creating a website using HTML, CSS, and just a hint of JavaScript. My primary focus right now is on building the ho ...

What is the best way to handle parsing JSON with special characters in JavaScript?

Content stored in my database: "Recommended cutting conditions" When using Json_encode in PHP, the result is: {"table1":[{"Item":{"original_text":"\u63a8\u5968\u5207\u524a\u6761\u4ef6 \b"}}]}; In JavaScript : var str ...

Error: Papa is not defined. The file was loaded from the CDN in the header section

I have integrated the cdn hosted lib for PapaParse in my HTML header. However, when I execute my JavaScript file and it reaches the function where I call Papa.unparse(data); It throws an error stating that Papa is undefined. This has left me puzzled as I h ...

Toggle the visibility of a div based on the id found in JSON data

I am looking to implement a JavaScript snippet in my code that will show or hide a div based on the category ID returned by my JSON data. <div id="community-members-member-content-categories-container"> <div class="commun ...

Issues with TypeScript Optional Parameters functionality

I am struggling with a situation involving the SampleData class and its default property prop2. class SampleData { prop1: string; prop2: {} = {}; } export default SampleData; Every time I attempt to create a new instance of SampleData without sp ...