Step-by-step guide on determining the argument type of a function from an external source

type AppState = {
  user: {
    firstName: string,
    lastName: string,
  }
}

const appState = {
  user: {
    firstName: 'John',
    lastName: 'Doe',
  }
}

type Action<T> = (state: AppState, payload: any) => T;

type Actions<T> = Record<string, Action<T>>;

type ActionOutput<T extends (...arg: any[]) => any> = T extends (arg: any, arg1: infer G) => any ? (payload: G) => any : (payload: any) => any;

type ActionsOutput<T> = Record<keyof T, ActionOutput<T>>; // how to pass acording function type?

function actionGenerator<T, G extends Actions<T>>(payload: G): {
  actions: ActionsOutput<G>
} {
  return {
    actions: Object.entries(payload).reduce((prev, [key, callback]) => {
      return {
        ...prev,
        [key]: (actionPayload) => {
          callback(appState, actionPayload);
        },
      }
    }, {} as ActionsOutput<G>),
  };
}

function updateUserDetailsAction(state: AppState, payload: {
  firstName: string,
  lastName: string,
}) {
  return {
    ...state,
    user: {
      ...payload,
    }
  }
}

const { actions } = actionGenerator<AppState, { updateNameAction: any }>({
  updateNameAction
})

actions.updateUserDetailsAction({
  test: '123'
})

How to infer a function's argument. so when the above example's action is invoked it will throw an error when the payload's type is not matched.

working in progress link.

Answer №1

Let me provide a clearer explanation.

Differences Between TypeScript and JavaScript

In TypeScript, most of the code you write gets stripped away at runtime, including types and generics. The only thing that remains are classes, which are converted to JavaScript. However, smart casting is not retained in this process.

class MyClass {
   constructor(readonly value: string){};
}

// Both definitions work in TypeScript
const correctInstantiation = new MyClass('the value');
const wrongInstantiation = { value: 'the other value' };

console.log(correctInstantiation instanceof MyClass); // returns true
console.log(wrongInstantiation instanceof MyClass); // returns false

You can use modifiers like readonly, public, and

private</code to define class fields directly in the constructor parameters for instance creation purposes.</p>
<h2>Challenges with Generics and Instanceof</h2>
<p>Unlike languages like Java, TypeScript lacks support for using generics with the instanceof operator due to the removal of generics at runtime. This necessitates specifying the type explicitly when needed.</p>
<pre><code>// This code won't compile
function wrongFunction<T>(payload: T) {
  return payload instanceof T; // Not allowed!
}

Solution Approach

To address this limitation, you could encapsulate actions within a wrapper class while defining specific types for payloads as demonstrated below:

// Example classes for payload types
class FunnyPayload {
  constructor(readonly pun: string) {}
}

class BoringPayload {
  constructor(readonly remark: string) {}
}

type ActionDefinition<PayloadType, OutputType> = {
  payloadType: any,
  action: Action<PayloadType, OutputType>,
}

// Dictionary of actions with payload types
const actionsDictionary = {
      ...
} as { [key: string]: ActionDefinition<any, any> };

You can then create a list of actions with payload validation by mapping over the dictionary entries:

  ...

Testing the Implementation

With these changes, you now have a setup where:

  ...

Click here to see the working example.

Lastly, it's advisable to use more descriptive names for generics, such as PayloadType and OutputType, to maintain clarity in your codebase.

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

How can I successfully pass the date value from react-datepicker to the react-hook-form registration?

Creating a registration form that includes a required birthday field using react-datepicker and react-hook-form. However, upon submitting the form, all data is logged in the console except for the birthDate, which remains null. How can I retrieve the date ...

What is the best way to retrieve an array stored within a key of a JSON object within an Angular service?

I have been learning about Angular heroes through a tutorial and decided to apply my knowledge in a small test project. While it worked fine with mock items using the built-in http server, I am facing difficulties retrieving items from a Django REST API. ...

Running a Redux Thunk action from within a TypeScript environment, beyond the confines of a React component

Currently, I am in the process of converting a React Native app into TypeScript. Unfortunately, I have encountered an issue with dispatching thunk actions outside of the store. Below is how my store is configured: store/index.ts import { createStore, app ...

The proxy request gets delayed unless I utilize the http-proxy-middleware

Here is the code for a provider: @Injectable() export class GameServerProxyService { private httpProxy: httpProxy; constructor(@Inject(GameServerDetailsService) private gameServiceDetailsService: GameServerDetailsService) { this.httpP ...

How can you export the type of a private class in TypeScript without exporting the class itself?

I am facing a dilemma in my module where the public method of a public class is responsible for creating and returning a new instance of a private class. The stipulation is that only MyClass should have the capability to instantiate MyClassPrivateHelper. ...

Having trouble retrieving spot price using Uniswap SDK due to a transaction error LOK

const quotedAmountOut = await quoterContract.callStatic.quoteExactInputSingle( immutables.token0, immutables.token1, immutables.fee, amountIn, 0 ) I set up a pool on Uniswap V3 for two ERC20 dummy tokens by using the createPool() met ...

What is the process of transforming two forms into components and then integrating those components into a view in Angular 5?

Currently, I have two forms running smoothly on the same component as shown in InfoAndQualificationComponent.ts import { Component, OnInit } from '@angular/core'; import { FormGroup, FormControl } from "@angular/forms"; @Component({ selector: ...

Utilizing Jest and nest.js for testing with absolute paths

Looking at my jest configuration inside the package.json: "jest": { "moduleFileExtensions": [ "js", "json", "ts" ], "moduleDirectories":["node_modules", "src" ...

Ways to modify this request in order to update the current status

How can I optimize these calls to avoid repeating the same sentence for refreshing the state? I'm not looking for a major overhaul, just some suggestions like putting this call inside a function and invoking it when needed... export const CategoriesPa ...

Exploring the capabilities of using Next.js with grpc-node

I am currently utilizing gRPC in my project, but I am encountering an issue when trying to initialize the service within a Next.js application. Objective: I aim to create the client service once in the application and utilize it in getServerSideProps (wit ...

Is every export automatically bundled as default by typescript?

import * as mongoModule from 'mongodb'; The code snippet above demonstrates the standard ES6 method of importing mongodb in Node.js. It is interesting to note that while using import mongodbModule from 'mongodb' is expected to result i ...

Creating mandatory reactive form fields in Angular 11's HTML code based on conditions

I am facing an issue with two select/dropdown fields in my form. The second dropdown field should render based on a condition *ngIf="selectedStdntList?.packages". However, the problem is that the submit form function stops working even when the c ...

Display HTML tags on an HTML page using TypeScript

In my angular application, I encountered an issue where I needed to call one component inside another component. Initially, I was able to achieve this by simply using the second component's selector in the HTML of the first component: html: <div&g ...

Send a variable from a next.js middleware to an API request

I've been attempting to pass a middleware variable to my API pages via "req" but have encountered some issues Even after trying to send the user token to pages using "req", it consistently returns null The middleware file in question is: pages/api/u ...

What is the best way to establish a connection between a child and parent component using a click event?

I am working on a scenario where I have two components interacting with each other. The parent component features a button, and upon clicking this button, the child component is disabled while also opening up to display its own button for closing. How can ...

Creating a Dynamic Value in Svelte with TypeScript

I am attempting to obtain a timestamp from a Date object in Svelte and TypeScript. My goal is for the timestamp to automatically update whenever the Date object changes, so I am trying to make it reactive. Below is the code snippet I have experimented with ...

Is it possible to manipulate an Angular #variableName in order to retrieve an ElementRef for an HTML element?

Suppose I have a scenario where I create a button like this: <button #myButton>My Button</button> ...and then use ViewChild in the following way: @ViewChild('myButton', { static: true }) createButton: ElementRef; In this case, creat ...

Tips for utilizing the React Redux useDispatch hook within a RTL test

After incorporating a redux dispatch, I am seeking to conduct testing on a React component. To achieve this, I have adopted a personalized render function from the "test-utils.ts" document: import { ReactElement } from 'react' import { render, Re ...

Debugging in Next.js and Sanity.io: "Client Components do not support async/await yet, only Server Components."

I am a beginner working on creating a website and e-commerce store using React, Next 14, Typescript, and Sanity as the CMS. I have been following a tutorial available at https://www.youtube.com/watch?v=g2sE034SGjw&t. Encountering the following error: ...

Is it possible to import in TypeScript using only the declaration statement?

Is there a way to use a node module in TypeScript without explicitly importing it after compilation? For example: I have a global variable declared in a file named intellisense.ts where I have: import * as fs from 'fs'; Then in another file, ...