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

Every time Grunt detects newer files, it automatically triggers the imagemin:dynamic task

I am working with a Gruntfile that looks like this: grunt.initConfig({ imagemin: { dynamic: { files: [ src: ['lib/public/img/*.{png,jpg,jpeg,gif}'], dst: 'build/public/img/', expand: true, fl ...

Perform a toggle action on the first row when clicking within the current row using Jquery

I've been grappling with the idea of creating a function that can display and hide a comment field when a button is clicked. The challenge here is that there are multiple line items with their own comment boxes. I want to find a way to achieve this wi ...

How jQuery stops the submission of a form

Sample HTML Code <p><select name="status" class="form-control" id="showThisItem"> <option value=""> Select Status </option> <option value="Paid"> Paid </option> <option value="Unpa ...

Dynamically scrolling using SuperScrollorama and Greensocks

I'm currently facing a JavaScript animated scroll challenge that has me scratching my head. My approach involves using the SuperScrollorama jQuery plugin, which relies on the Greensock JS tweening library. The main effect I'm aiming for is to " ...

Choose a specific example using the class name in nicEdit

Is there a way to select an instance using nicEdit based on its class name rather than just its id? For example: <div class="myInstance1"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed magna dolor, faucibus ac, iaculis non, cursus et ...

Using Vue.js to send user input data to a different page and trigger a method for submission

I am seeking assistance with my first question and hope to receive your support. In my setup, I have a catalogue page that includes a keyword search function and a main page with a search bar. My objective is to automatically redirect any submission from ...

"Vue Filepond: Enhancing Your Images with Cropping

Currently, I am integrating filepond with VueJS to facilitate image uploads. To enable image cropping during upload, a specific configuration is required. The filepond plugin has been globally registered, as shown below: import Vue from 'vue'; i ...

Changing the Value of an Input Element Dynamically in React: A Step-by-Step Guide

In a scenario where I have a component that takes an element, such as <input />, and I need to update its value programmatically after 15 seconds. Initially, I had the following approach in mind: const MyComponent = (myInput: JSX.Element) => { ...

Attempting to bring in HTML through a helper, but Rails doesn't seem too thrilled about it

I have a form that triggers a remote GET request, leading to the display of a modal. The issue I'm facing is that multiple actions can utilize the same model, so I am attempting to employ a helper and jQuery to showcase different data based on what is ...

Lightbox.options does not exist as a function within the lightbox plugin

I recently incorporated a lightbox plugin into my website, which can be found at the following link: For displaying items on the page, I am using markup similar to this example: <a href="images/image-2.jpg" data-lightbox="my-image">Image #2</a&g ...

Drawing recursive 2D tree fractals using WebGL technology

Attempting to create a binary fractal tree in webgl, but struggling with the branch angles not aligning as desired. The approach involves plotting vertex points into an array, converting it into a float32array, and then utilizing drawArrays(LINE_STRIPE) fo ...

What is causing the regular expression to fail when using the OR operator?

Here is the code snippet I've been working on: function toCamelCase(str){ var rest = str.replace((/-/)|(/_/)g, "") ; document.write(rest); } toCamelCase("the-stealth_warrior"); When running this code, I receive an error message: Uncaught Syntax ...

Using WebSockets in Angular 4

Currently in the process of developing a chat application using Angular 4 and WebSocket. I found guidance from this Angular WebSocket tutorial This is the source code for the WebsocketService: import { Injectable } from '@angular/core'; import ...

When using Next.js, I have found that the global.css file only applies styles successfully when the code is pasted directly into the page.tsx file. However, when attempting to

I recently started exploring nextjs and came across this video "https://www.youtube.com/watch?v=KzqNLDMSdMc&ab_channel=TheBraveCoders" This is when I realized that the CSS styles were not being applied to HeaderTop (the first component cre ...

Template for Gatsby's alternative layout design

I have recently developed a template for pages that showcase a single product (blog-post.js). However, I now require a different type of page with its own template (category-post.js) to display all products within a specific category. Since the number of c ...

Tips for accelerating the loading of data retrieved through ajax requests

At present, data is being fetched in array form from a PHP script. I have noticed that when retrieving 40 sets of data, it takes approximately 20 seconds for the data to load. This delay may be due to ajax having to wait until all the results are gathered. ...

Utilize ZLIB and Node.js to create a compressed zip archive of a folder's contents

I need to compress all the files in a directory into a single ZIP file. Currently, I am using this code snippet: var fs = require('fs'); var tar = require('tar'); var zlib = require('zlib'); var path = require('path&apo ...

Issue with jQuery fadeIn() and fadeOut() functions on IE versions 7 and 8

I have a project in Ruby on Rails that showcases illustrations. The top of the page features category links that fade out the current illustrations, replace them with new content, and then fade back in. Currently, I am utilizing jQuery version 1.6.2 for t ...

Tips for swapping out textures imported from Collada with ShaderMaterial textures in Three.js

Is it possible to update the textures of a basic 3D model loaded using the Collada loader in the Three.js library? My goal is to incorporate color, specular, and normal maps onto the model using ShaderMaterial by referencing new files with the model' ...

The callback functions, such as afterMove, are not being executed

This code snippet is copied from Owl Carousel's official website. I am having trouble getting the callback functions like afterMove to work. Can anyone help me figure out why the afterMove function is not being called? It seems that none of the callba ...