Discover the return types in Typescript by inferring from a collection of functions within an

Imagine you have an object

{
  num1: 1,
  num2: 2,
  str: "abc"
}

Your goal is to develop a function that accepts any similar object as the first parameter, and a custom selectors object as the second parameter.

function fn<O extends object, S extends object>(
    obj: O,
    selectors: {
        [K in keyof S]: {
            selector: (obj: O) => InferReturnType;
            equals: (a: InferReturnType, b: InferReturnType) => boolean;
        }
    }
) {
}

The objective is to automatically deduce the parameters a and b of the equals function based on the return type of the selector.

For instance,

fn(
    { num1: 1, num2: 2, str: "abc" },
    {
        selector1: {
            selector: ({ num1, num2 }) => ({ num1, num2 }),
            equals: (a, b) => a.num1 === b.num1 && a.num2 === b.num2,
        },
        selector2: {
            selector: ({ num1, str }) => ({ num1, str }),
            equals: (a, b) => a.num1 === b.num1 && a.str === b.str,
        }
    }
)

Typescript playground

Answer №1

At this moment, achieving this is not possible and there has been a feature request submitted at ms/TS#53018

However, there is a workaround available, although it may not be widely documented. The workaround involves creating a dummy function solely for the purpose of allowing the compiler to infer the type. While the compiler can recognize the type of the equals field, it does not automatically infer the type of the argument in the implementation of equal. To address this, we can define a separate generic function that takes a selector and returns it back unchanged.

To start, let's define a Selector type with two optional generic parameters, both defaulting to any. The first parameter represents the state's type, while the second represents the return type of the selector function:

type Selector<O = unknown, S = unknown> = {
  selector: (state: O) => S;
  equals: (a: S, b: S) => boolean;
};

Next, we can apply this in the fn function:

function fn<O extends object, S extends Record<string, Selector<O>>>(
  obj: O,
  selectors: S,
) {}

It's important to note that we pass O to Selector, which enables the compiler to propagate the type to the function we're about to create.

The dummy function:

const createSelector = <O, S>(selector: Selector<O, S>) => selector;

Usage example:

fn(
  { num1: 1, num2: 2, str: 'abc' },
  {
    selector1: createSelector({
      selector: ({ num1, num2 }) => ({ num1, num2 }),
      equals: (a, b) => a.num1 === b.num1 && a.num2 === b.num2,
    }),
  },
);

Try it out on the 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

Strategies for handling uncaught promise rejections within a Promise catch block

I'm facing a challenge with handling errors in Promise functions that use reject. I want to catch these errors in the catch block of the Promise.all() call, but it results in an "Unhandled promise rejection" error. function errorFunc() { return ne ...

Error Message: Fatal error encountered - TS6046: The value provided for the '--moduleResolution' option is invalid. Valid options include: 'node', 'classic', 'node16', 'nodenext

As I develop a next.js web app with typescript and tailwind CSS, I encountered an issue. When running yarn dev in the terminal, I received this error message: FatalError: error TS6046: Argument for '--moduleResolution' option must be: 'node ...

Adding a new value to a string variable using TypeScript

Just starting out with Angular and I'm looking to capture user input from a text box with each keystroke and add it to a string variable like in Java. In my text box, I have the following setup for capturing key events: <input type="text" ...

typescript - transforming text into numerical values

newbalance = (Number(this.balance)) + (Number(this.pastAmount)); The result for my newbalance calculation is coming back as undefined, even though this.balance is 34 and this.pastAmount is 23. I've set this up in the controller and I'm trying t ...

Having difficulties injecting a Service into a TypeScript Controller

I recently started working with TypeScript and I created a controller where I need to inject a service in order to use its methods. However, I am facing an issue where I am unable to access the service functions and encountering an error. Error TypeError ...

Preventing Event Propagation in Angular HTML

I am encountering an issue with stopPropagation, and I need assistance with implementing it in HTML and TypeScript for Angular. The problem is that the dialog opens but also triggers a propagation. Below is my code snippet in HTML: <label for="tab-two ...

Dynamic Angular component loading with lazy loading

In my Angular 4.1.3 project, I am currently developing a mapping application that incorporates lazy-loading for various tool modules. At present, the tools are loaded within the map using a router-outlet. However, I now need to expand this functionality to ...

Is it possible for NodeJS to prioritize IPv6 DNS lookups as the default option?

In my work with multiple TypeScript (NodeJS 14) client applications that are all Dockerized, I primarily use axios for most HTTP requests. However, there are other tools used as well. Typically, DNS queries default to resolving IPv4 addresses, resulting i ...

Leveraging the power of react-hook-form in combination with the latest version 6 of @mui

When using MUI v5 date pickers, I utilized the following method to register the input with react-hook-form: <DatePicker ...date picker props renderInput={(params) => { return ( <TextField {...params} ...

Incorporating external Angular 4 components within my custom components

For my project, I am attempting to incorporate jqWidgets Angular components: import { Component, ViewChild } from '@angular/core'; import 'jqwidgets-framework'; import { jqxTreeComponent } from "jqwidgets-framework/jqwidgets-ts/angula ...

A step-by-step guide on importing stompjs with rollup

My ng2 app with TypeScript utilizes stompjs successfully, but encounters issues when rollup is implemented. The import statement used is: import {Stomp} from "stompjs" However, upon running rollup, the error "EXCEPTION: Stomp is not defined" is thrown. ...

Navigating API data conversion on the frontend using Object-Oriented Programming

Currently, I am facing a challenge while developing the frontend of a web application using TypeScript. The dilemma revolves around efficiently converting a data object from an API response into a format suitable for the application. Let's consider r ...

The functionality to disable the ES lint max length rule is malfunctioning

In trying to disable an eslint rule in a TypeScript file, I encountered an issue with a regular expression that exceeded 500 characters. As a result, an eslint warning was generated. To address this, I attempted to add an eslint comment before declaring th ...

Achieving intellisense functionality in TypeScript without the use of classes

Just dipped my toes into TypeScript, attempting to convert this basic JavaScript code to TypeScript. Here is the JavaScript code snippet: Item = {} Item.buy = function (id) {} Item.sell = function (id) {} I prefer not to use classes and would like to ut ...

How to specifically activate window resize when the width changes

I am looking to implement custom styling on an element based on the current screenWidth of the window. This can be achieved using the following @HostListener: @HostListener('window:resize', ['$event']) public resize() { // Apply ...

The React Component in Next.js does not come equipped with CSS Module functionality

I have been attempting to incorporate and apply CSS styles from a module to a React component, but the styles are not being applied, and the CSS is not being included at all. Here is the current code snippet: ./components/Toolbar.tsx import styles from & ...

Overriding TypeScript types generated from the GraphQL schema

Currently, I am utilizing the graphql-code-generator to automatically generate TypeScript types from my GraphQL schema. Within GraphQL, it is possible to define custom scalar types that result in specific type mappings, as seen below in the following code ...

Combine iron-page and bind them together

Recently, I've started learning about Polymer and I want to bind together paper-tabs and iron-pages so that when a tab is clicked, the content loads dynamically. After going through the documentation, this is what I have tried: <app-toolbar> ...

Having trouble importing the hash-set module in TypeScript/SystemJS?

In the midst of developing an Aurelia project with TypeScript to generate JavaScript, I decided to incorporate another custom library called 'hash-set' (installed using jspm install npm:hash-set --save). However, I encountered difficulties in act ...

Spring Boot receiving null values from Angular form submission

I am currently working on a form in Angular that is used to submit information such as author, context, and recently added images. However, I have run into an issue where I am able to successfully retrieve the author and context, but not the images (it alw ...