TS: How can we determine the type of the returned object based on the argument property?

Assume we have the following data types

type ALL = 'AA' | 'BB' | 'CC';
type AA = { a: number; };
type BB = { b: string; };
type CC = { c: boolean; };
type MyArg = { type: ALL };

I attempted to create a mapping between type name and type as follows

type ReturnMap = {
  'AA': AA;
  'BB': BB;
  'CC': CC;
}

There is also a function called hook that should return an object based on the argument type

const hook = (g: MyArg) => {
  const some = {};
  ...// some calculations here
  return some as ReturnMap[g.type];
};

However, I encountered a TypeScript error on the line with the return statement

Type 'any' cannot be used as an index type.

If I modify the return statement like this

const hook = (g: MyArg) => {
  const some = {};
  ...// some calculations here
  return some as ReturnMap[typeof g['type']];
};

The returned object type in case of

const answer = hook({ type: 'BB' });

will be AA | BB | CC, but my intention is to get just BB;

Answer №1

It is important to keep in mind that the TypeScript type system operates when you convert the program into JavaScript. Once it reaches JavaScript, most types are no longer present in your code.

In addition, the values in your code remain unknown until after you execute the compiled JavaScript program, long after the TypeScript compiler has completed its task.

Therefore, you cannot establish types based on values that are only revealed during runtime. The types that can affect your code are limited to those you have defined and narrowed down in your code editor before any execution.

You may attempt something similar to what you are aiming for, but it likely will not provide the level of functionality you desire:

var map: {
    'AA': { a: number },
    'BB': { b: string },
    'CC': { c: boolean }
};

function h<T extends 'AA' | 'BB' | 'CC'>(ty: T): typeof map[T] {
    return {} as any;
}


var q = h('BB');
// q holds the type { b: string } because the argument ty was specified as a specific string literal value

Check out this live example. Note how the generated JavaScript code lacks all type information and associated logic related to types.

Answer №2

Approached the solution in the following manner

type RequestTypes = 'AA' | 'BB';
type RequestGenModel = {
  message: string;
  type: RequestTypes;
};

type RequestGen1Props = { alpha: string, beta: string };
const requestGen1 = (props: RequestGen1Props): RequestGenModel => {
  return {
    message: props.alpha + props.beta,
    type: 'AA',
  };
};

type RequestGen2Props = { delta: string };
const requestGen2 = (props: RequestGen2Props): RequestGenModel => {
  return {
    message: props.delta + '-DEFAULT',
    type: 'BB',
  };
};

Created a function named getHook with different variations

const placeholder = (x: any): unknown => placeholder;

function getHook(arg: ReturnType<typeof requestGen1>): AA;
function getHook(arg: ReturnType<typeof requestGen2>): BB;
function getHook(arg) {
  const result = placeholder(arg);

  return result;
};

Therefore

const resultType = getHook(requestGen1({alpha: 'ggg', beta: 'cc'}));
// const resultType: AA
enter code here

UPDATE:

type ResultReturn<T> = T extends ReturnType<typeof requestGen1>
  ? AA
  : T extends ReturnType<typeof requestGen2>
    ? BB
    : never;

function getHookV2<T>(arg: T): ResultReturn<T> {
  const resultVar: any = placeholder(arg);

  return resultVar;
};

const finalResult = getHookV2(requestGen1({alpha: 'ggg', beta: 'cc'}));
// const finalResult: AA

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

Display and conceal automatically created input box using JavaScript

I am facing an issue with a form that contains a select dropdown and an input box. The input box should only be visible when the "Other" option is selected from the dropdown. The problem arises when a button clones this code, creating additional copies of ...

Transferring user-selected values from JavaScript to a PHP file

When sending values from JavaScript to a PHP file, everything works smoothly when all the values are collected. Step1 functions perfectly as both fields are mandatory. However, in Step2, values are only sent when all the fields are selected. There are co ...

The information returned to the callback function in Angular comes back null

In my Node.js application, I have set up an endpoint like this: usersRoute.get('/get', function(req, res) { //If no date was passed in - just use today's date var date = req.query.date || dateFormat(new Date(), 'yyyy-mm-dd&ap ...

Looking to verify a disabled select element and adjust the opacity of that element when it is disabled

$_product = $this->getProduct(); $_attributes = Mage::helper('core')->decorateArray($this->getAllowAttributes()); ?> <?php if ($_product->isSaleable() && count($_attributes)):?> <dl> <?php foreach($_attrib ...

The tab indicator in Material-UI fails to update when the back button is clicked

My code is currently functioning well: The tab indicator moves according to the URL of my tab. However, there is a peculiar issue that arises when the back button of the browser is pressed - the URL changes but the indicator remains on the same tab as befo ...

Using the Amazon Resource Name (ARN) of a Cloud Development Kit (CDK) resource in a different

Having trouble obtaining the ARN of my AWS CDK stack's Step Functions state machine for my lambda function. The ARN is constantly changing and I'm unsure how to access it. I attempted to create a .env file alongside the lambda function's in ...

Progressive Web App with Vue.js and WordPress Rest API integration

When creating an ecommerce website using Wordpress, I utilized Python for scraping data from other websites to compare prices and bulk upload products through a CSV file. My next goal is to learn Vue and transform the website into a PWA as it will be esse ...

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 ...

What are the benefits of sharing source files for TypeScript node modules?

Why do some TypeScript node modules, like those in the loopback-next/packages repository, include their source files along with the module? Is there a specific purpose for this practice or is it simply adding unnecessary bulk to the module's size? ...

difficulty receiving the information promptly via an AJAX request (utilizing AJAX and the 'for' loop)

Currently, I am successfully retrieving data from an ajax call for individuals. However, my next task is to retrieve multiple sets of data simultaneously. Here is the code snippet: for(var i = 1; i <= 2; i++){ console.log(i); $.ajax({ url: cal ...

Troubleshooting a Node.js problem with variable scope

I'm working on a nodejs route that downloads a URL as an mp3 using npm-youtube-dl. I have a download directory being monitored with chokidar for new files, and once a file is added, I save the file link. After the download completes, a function is cal ...

What is the process for storing form data into a text file?

Despite seeing similar questions in the past, I am determined to get to the bottom of why this code isn't functioning as expected. My goal is to input form data into a .txt file using a post request. While I lack extensive knowledge of PHP, I am pieci ...

Promise.all() ensures that all promises in the array are resolved with the same value

Currently, I'm in the process of developing a module that contains a function designed to return a promise using Promise.all() for the purpose of sending emails to multiple users. By simplifying the code, I have managed to isolate and reproduce the is ...

Start numerous nodejs servers with just a single command

I currently have multiple Nodejs servers, each stored in its own separate folder within a root directory. Whenever I need to run these servers, I find it cumbersome to navigate through each folder and manually type nodemon *name*. The number of servers i ...

What is the process for retrieving a value from a Django view?

Would it be feasible to call a view from a JavaScript file using Ajax and have the view only return a specific value known as "this"? However, despite attempting this, an error occurs stating that the view did not provide an HttpResponse object, but instea ...

Using socket.io-client in Angular 4: A Step-by-Step Guide

I am attempting to establish a connection between my server side, which is PHP Laravel with Echo WebSocket, and Angular 4. I have attempted to use both ng2-socket-io via npm and laravel-echo via npm, but unfortunately neither were successful. If anyone h ...

Understanding the separation and communication techniques in Vue.js components

I recently developed a small vuejs application and I find myself getting confused with the functioning of components. Here is the code snippet that I have been working on: <div id="app"> <div v-if="loggedIn"> <nav> <r ...

The function dataTable is not recognized as a valid command

I am encountering an issue while attempting to utilize the datatables plugin. Whenever I call the function dataTable(), I receive an error. Here is a snippet of my code: @Scripts.Render("~/Scripts/DataTables-1.9.4/media/js/jquery.js") @Scripts.Render("~/S ...

"pre and post" historical context

Is there a way to create a stunning "Before and After" effect using full-sized background images? It would be amazing if I could achieve this! I've been experimenting with different examples but can't seem to get the second 'reveal' di ...

Generating symbols that combine both images and text seamlessly

I am working with a circle image that I need to resize based on its placement. This image is intended to be a background for a character or two of text. Specifically, whenever the number 1 or 2 appears in a certain element, I want the circle to appear beh ...