Issue with inferring types in Typescript decorators

I'm encountering an issue with a TypeScript decorator related to typing function parameters in a Redux context. The decorator in question is @OnAction, which intercepts actions based on their type.

My goal is to specify the type for the decorator so that it requires a function parameter corresponding to the action type. For example, using

@OnAction('history/modal/toggle')
, I expect the decorator to require a function with a parameter action: HistoryModalToggleAction.

However, I am running into a compile error with my current codebase involving this decorator.

Playground

// Sample.ts

export interface HistoryModalReducerProps {
  visible: boolean;
  otherStuff: string;
}

export interface HistoryModalToggleAction extends Action<'history/modal/toggle'> {
  visible: boolean;
}

export type StoreAction = HistoryModalToggleAction | Action<'otherAction1'> | Action<'otherAction2'>;

export class Sample {

  @OnAction('history/modal/toggle')
  onToggle(state: Readonly<HistoryModalReducerProps>, action: HistoryModalToggleAction): Readonly<HistoryModalReducerProps> {

    if (!action.visible)
      return state;
    else
      return {
        ...state,
        visible: true
      };
  }

}
// OnAction.ts

type RestrictedMethod<A extends StoreAction> = (
  prototype: any,
  key: string,
  descriptor: TypedPropertyDescriptor<(
    state: any,
    action: A
  ) => any>
) => void;

export function OnAction<A extends StoreAction>(type: A['type']): RestrictedMethod<A> {

  const restrictedMethod: RestrictedMethod<A> = (prototype, key, descriptor) => {

    // ...

  };

  return restrictedMethod;
}

The compile error indicates that my function expects StoreAction while receiving HistoryModalToggleAction. Despite being compatible types, the error persists. My TypeScript version is 3.5.3.

I appreciate any assistance or guidance on resolving this issue.

Answer №1

Solution discovered!

To make it work, I had to modify the signature of OnAction and narrow down the action type:

type NarrowAction<T, N> = T extends Action<N> ? T : never;

export function OnAction<T extends StoreAction['type']>(type: T): RestrictedMethod<NarrowAction<StoreAction, T>> {

  return (prototype, key, descriptor) => {

    // ...

  };
}

After the change, everything is functioning correctly! Check out this Playground to see it in action.

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

Misplacing this pointer within the $scope.$on event

After registering the "$routeChangeSuccessEvent" from AngularJS and setting the callback function, I encountered an issue where I couldn't access my controller's instance using "this". It appears that the current this instance is undefined. The ...

Using React.ReactNode as an argument in Storybook

This is a unique button component type that I have created import React from 'react' export type ButtonProps = { label: string; color?:'primary' | 'secondary' | 'tertiary'; size?:'mobile' | 'tabl ...

Managing circular dependencies in React useEffect functions

Dealing with a React hook that is in charge of downloading and saving file URLs into a local cache can be quite challenging. The main issue I'm currently facing is finding an effective way to detect changes in the cacheQueue state. Whenever a new item ...

Encountering an ECONNREFUSED error upon upgrading from Next.js 12 to 13

After upgrading from Nextjs 12 to 13, I am experiencing issues where every time I try to run the application, I encounter ECONNREFUSED to my local host but the port seems to keep changing. This results in the application not rendering properly. > <a ...

Can you specify the necessary import statement for CallableContext?

My Google Cloud function is simple and looks like this: import * as functions from 'firebase-functions'; var util = require('util') export const repeat = functions.https.onCall( function (data, context) { console.log(&apo ...

Struggling to render the template inside a .vue file in a Vue.js + TypeScript project?

Is anyone familiar with setting up a Vue TS based project? I have encountered an issue where the template data is not being rendered in the browser's DOM. The project structure can be found in this repository: https://github.com/AndrewBogdanovTSS/typ ...

Error message in Typescript: The argument type '() => () => Socket<DefaultEventsMap, DefaultEventsMap>' cannot be assigned to a parameter of type 'EffectCallback'

I am struggling to comprehend how I should specifically type constrain in order to prevent the error within my useEffect. One approach is to either cast my newSocket or specify the return value of my useEffect as any, but I am hesitant about that solution. ...

Leveraging i18next to substitute a variable with a ReactNode component

My translation json file contains the following translation: "pageNotFound": { "description": "The page could not be found. Click {{link}} to return to the home page" }, I am looking to replace the link variable with a ReactRouter <Link> Within ...

Having trouble editing a form with React Hooks and Redux?

Seeking assistance with creating an edit form in React that utilizes data stored in Redux. The current form I have created is displaying the values correctly, but it appears to be read-only as no changes are being reflected when input is altered. Any advic ...

What is the best method for snapshot testing a component that includes nested components?

I am currently testing a component that contains a nested component using Redux. To test this, I am utilizing shallow rendering. Below is the code for my test: describe("Header", () => void it("renders correctly", () => { const renderer = new ...

Storing the value of e.currentTarget in a TypeScript variable with a fixed data type

Within my interface, MyObject.type is designated as a type of (constant): 'orange' | 'apple'. However, when attempting to execute: MyObject.type = e.currentTarget.value in the onChange method, an error arises since it could potentially ...

What is the process for integrating the node-menu package into my project without utilizing the require statement?

Is there a way to incorporate node-menu into my TypeScript project without using require, like this: const menu = require('node-menu'); Whenever I attempt to import node-menu into my project, I encounter the following errors: https://i.sstatic. ...

Heroku: Unable to retrieve resource - server returned a 404 (Not Found) status code

I successfully developed my Angular 6 app on my local machine with everything working perfectly. However, after deploying the project to Heroku and running my app here Testing App, I encountered an error in the console browser. Failed to load resource: ...

What is the location for adjusting the angular strictness flags that determine the level of strictness for strictTemplates?

Currently in the process of transitioning our application to strictTemplates, we are encountering a multitude of errors, some more significant than others. As a result, I decided to adjust the strictness of the angular type checker and came across these s ...

Issue with Angular 2: Unable to display 404 error page

I have created a custom "404 - page not found" page to handle situations where the user enters a URL that does not match any paths in my web app: export const routerConfig: Routes = [ { component: LandingPageComponent, path: "", }, { ...

Angular 2/4: Struggling to refresh child component's view

I need assistance with updating the value of "str" in the child component's view from the parent component. I want to do this by calling the "change()" function in the child component. Here is my code: import { Component } from '@angular/core&ap ...

The issue arises when Jest fails to align with a custom error type while utilizing dynamic imports

In my project, I have defined a custom error in a file named 'errors.ts': export class CustomError extends Error { constructor(message?: string) { super(message); Object.setPrototypeOf(this, Error.prototype); this.nam ...

The nested createEffect does not trigger again

It seems like I may not have accurately conveyed my issue, but here it is: When the render signal is triggered, an object creation process occurs that generates additional effects dependent on the update signal. However, the scope appears to be disposed a ...

Tips on looping through a dynamic FormControl using Angular2 and FormBuilder

I am facing an issue when trying to iterate over a dynamically generated FormControl in the HTML section. Instead of displaying the expected values, I am getting [object Object],[object Object] in the input field. Here is the provided data structure: { ...

What is the process for extracting TypeScript types from GraphQL query and mutation fields in order to get args?

I am experiencing difficulties with utilizing TypeScript and GraphQL. I am struggling to ensure that everything is properly typed. How can I achieve typed args and parent properties in Root query and mutation fields? For instance: Server: export interfa ...