Deduce the generic types of conditional return based on object property

My goal is to determine the generic type of Model for each property. Currently, everything is displaying as unknown[] instead of the desired types outlined in the comments below.

playground

class Model<T> { x?: T }

type ArgumentType<T> = T extends Model<infer TInner> ? Model<TInner> : (
        T extends Model<infer TInner>[] ? Model<TInner>[] : never
    );

type ReturnType<T> = T extends Model<infer TInner> ? TInner[] : (
        T extends Model<infer TInner>[] ? TInner[][] : never
    );

const test = <TInner, TValue extends ArgumentType<TInner>, T extends Record<string, TValue>>(models: T): Record<keyof T, ReturnType<TValue>> => {

        return;
    }

const numberModel = new Model<number>();
const stringModel = new Model<string>();
const bigintModel = new Model<bigint>();

const result = test({
    prop1: [numberModel, numberModel, numberModel],
    prop2: [stringModel],
    prop3: stringModel,
    prop4: [numberModel, numberModel, numberModel, bigintModel],
    prop5: stringModel
});

//expected types
result.prop1 // number[][]
result.prop2 // string[][]
result.prop3 // string[]
result.prop4 // (number | bigint)[][]
result.prop5 // string[]

//actual types
result.prop1 // unknown[]
result.prop2 // unknown[]
result.prop3 // unknown[]
result.prop4 // unknown[]
result.prop5 // unknown[]

Answer №1

Here is a suggestion for your test() function with a revised call signature:

declare const test: <T extends Record<keyof T, Model<any> | Model<any>[]>>(
  models: T
) => { [K in keyof T]:
    T[K] extends Model<infer U> ? U[] :
    T[K] extends Model<infer U>[] ? U[][] :
    never
}

In this updated version, we utilize a single generic type parameter T constrained to

Record<keyof T, Model<any> | Model<any>[]>
, which simplifies the inference process for TypeScript's compiler.

The output type definition is based on the properties of T, similar to the ReturnType<> alias but mapped over each property key K corresponding to models.


For testing purposes, consider the following example:

const numberModel = new Model<number>();
const stringModel = new Model<string>();
const bigintModel = new Model<bigint>();

const result = test({
  prop1: [numberModel, numberModel, numberModel],
  prop2: [stringModel],
  prop3: stringModel,
  prop4: [numberModel, numberModel, numberModel, bigintModel],
  prop5: stringModel
});

/* Result:
    prop1: number[][];
    prop2: string[][];
    prop3: string[];
    prop4: (number | bigint)[][];
    prop5: string[];
*/

Test successful!

Link to playground with 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

What are the steps to generate two unique instances from a single class?

Is there a way to output two instances of the class Cat : Skitty, 9 years and Pixel, 6 years, in the console? (() => { class Cat { constructor(name, age) { this.name = name; this.age = age; } } docume ...

sending data between pages in a Next.js application

I'm working on a project that consists of two pages: test1 and test2. I want to pass a prop from page 1 to page 2 without using the useRouter hook or setting it as a query string. In my test1 page, I have a color variable defined as const with a value ...

The function grunt.task.run() is malfunctioning

Currently, I am experimenting with integrating Grunt into my Express application. Here is a snippet of the code I have: var grunt = require('grunt'); require(process.cwd() + '/gruntfile.js')(grunt); grunt.task.run('development&ap ...

What could be causing the function to not execute before the rest of the code in the React App?

My lack of expertise may be the reason, but I'm unsure how to address this issue: Here's what I have: A button labeled "Check numbers" <Button fullWidth variant="contained" onClick={this.checkOptOut ...

Identifying when an element hovers over a particular section

I am facing an issue with the side dot navigation on my website. The navigation is standard with a fixed position, but I also have two types of sections with different backgrounds - one white and the other black. The problem arises when the dots are not vi ...

Retrieve the country code of an IP address using getJSON

I am currently facing an unusual issue. My goal is to extract the country code (like US for United States) from an IP address using free APIs. After some research, I came across ipify for retrieving the IP address (which works fine), and then attempted to ...

Obtaining a Variable Element through Selector

When working with Puppeteer, I am faced with the challenge of clicking on a web button that has a dynamic id like: #product-6852370-Size. Typically, I would use the following code: page.click('#product-6852370-Size'); However, the number withi ...

Adjusting data points on a radar chart.js through user interaction

I have a radar map generated using chart.js, along with some input fields where users can enter values that I want to reflect in the graph. I am exploring ways to update the chart dynamically as the user types in the values. One option is to have the char ...

How can I activate the copy function in jQuery?

I'm trying to figure out how to initiate a copy event using JavaScript or jQuery. I need to be able to simulate the copy event by clicking on a button, but I haven't been able to find a solution yet. I want to avoid using ZeroClipboard or any oth ...

The Angular $http.jsonp() function can only be executed one time

Upon the first response being successful (alert->done), any subsequent hits will result in an 'error' response. I attempted to resolve this issue by adding some config parameters with 'cache: false', but it still only works the firs ...

In just a single line of code, you can iterate through a Record object and retrieve an array of DOM elements

I am working with an object type MyType = 'name' | 'base' | 'six'; obj: MyType = { 'name': {key: 'm1'}, 'base': {key: 'm2'}, 'six': {key: 'm3'}, } My goal is ...

Is it possible to modify the background-image using Typescript within an Angular project?

I have been struggling to change the background image in my Angular project using Typescript. I attempted to make the change directly in my HTML file because I am unsure how to do it in the CSS. Here is the line of code in my HTML file: <div style="ba ...

Is there a sweet TypeScript class constructor that can take in its own instance as an argument?

I have a scenario where I need to read in instances of Todo from a CSV file. The issue is that Papaparse does not handle dynamic conversion on dates, so I'm currently dropping the object into its own constructor to do the conversion: class Todo { ...

Tips on obtaining the current date format (e.g. dd/mm/yyyy or mm/dd/yyyy) on the client side

I am currently working with the datepicker control, and I need to adjust the date format based on my browser's settings - either dd/mm/yyyy or mm/dd/yyyy. For example: if my browser date is 22/06/2019, then I should use "dd/MM/yyyy" format in the dat ...

Converting a PHP array to a JavaScript array causes an issue of undefined variable when attempting to access a PHP array that has

I've been searching through other questions without finding the answer, so I'm reaching out for help with my specific issue. My problem is transferring a php array to a javascript array. In my code editor (phpStorm), I'm getting an error st ...

How can I create an Array of objects that implement an interface? Here's an example: myData: Array<myInterface> = []; However, I encountered an issue where the error message "Property 'xxxxxx' does not exist on type 'myInterface[]'" appears

Currently, I am in the process of defining an interface for an array of objects. My goal is to set the initial value within the component as an empty array. Within a file, I have created the following interface: export interface myInterface{ "pictur ...

Using cannonjs and Three.js to connect a physics body with a visual mesh

I have written the code below to create two spheres using cannonjs with Three.js for rendering. var world, mass, body, shape, timeStep=1/60, camera, scene, renderer, geometry, material, mesh; initThree(); initCannon(); animate(); func ...

Poorly packaged library - Custom Angular library - Node Package Manager

Recently, I've been delving into the process of publishing a simple Angular library on NPM. Despite following various tutorials (like those found here, here, and here), I faced difficulties when attempting to use it in a test project. MY JOURNEY In s ...

Position the caret after adding a new element in a content-editable div

I have a contenteditable div that contains various elements. My goal is to automatically create a new paragraph tag right after the element where the cursor is positioned when the user presses the enter key. Currently, I am able to insert the paragraph tag ...

Creating a circular frame for your image to use as a Facebook profile picture

In the process of developing an input feature that allows users to upload file images for avatar changes, similar to Facebook's functionality. However, I am aware that Facebook utilizes a circular area for avatars and enables users to adjust the image ...