"Trouble with Typescript's 'keyof' not recognizing 'any' as a constraint

These are the current definitions I have on hand:

interface Action<T extends string, P> {
  type: T;
  payload: P;
}

type ActionDefinitions = {
  setNumber: number;
  setString: string;
}

type ActionCreator<A extends keyof ActionDefinitions> =
  () => Action<A, ActionDefinitions[A]>;

The plan is to use ActionCreator<any>, with any being restricted to a key within ActionDefinitions. This restriction is crucial when constructing a type like this:

type Creators = {
  [K: string]: ActionCreator<keyof ActionDefinitions>
};

This requires automatic type deduction for each entry in the object. For instance, creating a Creators object as follows:

const testCreators: Creators = {
  foo: () => ({
    type: "setNumber",
    payload: "string",
  })
}

An error should be triggered in this scenario because foo needs to be inferred as an

ActionCreator<"setNumber">
, which means it must align with the type
Action<"setNumber", number>
, indicating that the payload should contain a number, not a string.

However, TypeScript simply permits any type for the payload, which appears incorrect since if the generic type A matches "setNumber," then foo ought to return

Action<"setNumber", ActionDefinitions["setNumber"]>
, specifically
Action<"setNumber", number>
...yet it accommodates any instead of requiring number.

Is there anyone aware of why this occurs or how it can be rectified?

Answer №1

One issue is that when you use keyof ActionDefinitions, you end up with a union type and passing it into ActionCreator results in the following type:

type ActionCreator = () => Action<keyof ActionDefinitions, string | number>

This allows for string | number as a payload.

What you should do instead is to iterate through the keys of keyof ActionDefinitions and pass concrete keys into ActionCreator:

type Creators = {
  [K: string]: { [I in keyof ActionDefinitions]: ActionCreator<I> }[keyof ActionDefinitions]
};

Check out playground link


Here's an updated version of the generic ActionCreator type:

type ActionCreator<T, K extends Extract<keyof T, string> = Extract<keyof T, string>> = {
  [I in K]: () => Action<I, T[I]>;
}[K]

type Creators = {
  [K: string]: ActionCreator<ActionDefinitions>;
};

Play around with it 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

React Native: Issue with the data section in FlatList

I encountered an issue while using Flatlist to address a problem, but I ran into an error with the data property of my Flatlist. The error message is not very clear and I'm having trouble understanding it ( No overload matches this call. Overload 1 of ...

Encountering difficulties importing in Typescript and ts-node node scripts, regardless of configuration or package type

I am facing a challenge with my React Typescript project where multiple files share a similar structure but have differences at certain points. To avoid manually copying and pasting files and making changes, I decided to create a Node script that automates ...

Here is an example showcasing how to use Angular 2 to make an

How can I correctly retrieve json data from an http get request in Angular 2? Currently, I am working on testing some local data with a mocked endpoint. Although I am able to see the result in the http.get() method, I am facing issues when trying to assign ...

webpack is having trouble locating the src file, even though it should not be searching for it in the first place

I'm currently delving into the world of using TypeScript with React and am following a helpful tutorial at: https://blog.logrocket.com/how-why-a-guide-to-using-typescript-with-react-fffb76c61614 However, when attempting to run the webpack command thr ...

I am having issues with the Okta Angular sign-in widget as it is not redirecting

Recently, I integrated the Okta angular sign-in widget into my project, but I encountered an issue. In my application, I have multiple modules including an authentication module that manages sign-in, sign-out, and sign-up functionalities. The route I ult ...

What is the best way to swap out the if else statement with a Ternary operator within a JavaScript function?

Is there a way to replace the if else statement in the function using a Ternary operator in JavaScript? private getProductName(productType: string): string { let productName = 'Product not found'; this.deal.packages.find(p => p.isSele ...

Angular 4 enum string mapping reversed

Here is an example of a string enum: export enum TokenLength { SIX = '6', EIGHT = '8', } I am trying to retrieve the string value 'SIX' or 'EIGHT' by reverse mapping this enum. I have attempted various methods: ...

Access Select without needing to click on the child component

I am curious to learn how to open a Select from blueprint without relying on the click method of the child component used for rendering the select. <UserSelect items={allUsers} popoverProps={{ minimal: false }} noResults={<MenuItem disabled={ ...

Error: Missing provider for MatBottomSheetRef

While experimenting in this StackBlitz, I encountered the following error message (even though the MatBottomSheetModule is imported): ERROR Error: StaticInjectorError(AppModule)[CountryCodeSelectComponent -> MatBottomSheetRef]: S ...

Utilizing a tuple for indexing in Typescript

Imagine you have a tuple containing keys like ["a", "b", "c"] and a nested object with these keys as properties {a: {b: {c: number}}}. How can you recursively access the members of the tuple as indices in typescript? An example implementation without prop ...

Implementing express-openid-connect in a TypeScript project

Trying to incorporate the express-openid-connect library for authentication backend setup with a "simple configuration," an issue arises when attempting to access the oidc object from express.Request: app.get("/", (req: express.Request, res: express.Respon ...

Is there a method to automatically eliminate all unnecessary properties in an Angular project?

In my extensive Angular project, I suspect that there are numerous declared properties in .component.ts that are not being used. Is there a method available to automatically eliminate all unused properties within an Angular project while also taking into ...

Navigational menu routing with AngularJS2 using router link paths

Currently, I am working on developing a navigation menu using angularJS2. Here is the snippet from my app.component.ts: import {provide, Component} from 'angular2/core'; import {APP_BASE_HREF, ROUTER_DIRECTIVES, ROUTER_PROVIDERS, HashLocati ...

Tips for efficiently finding and comparing text within the results generated by a for loop in Angular/Javascript

In my XML data, I have extracted all the tag names using a for loop and some other logic. Now, I am looking to find the word 'author' from the output values that are displayed in the console during the loop. If any of the output values match &apo ...

When utilizing the package, an error occurs stating that Chart__default.default is not a constructor in chart.js

I have been working on a project that involves creating a package with a react chart component using chart.js. Everything runs smoothly when I debug the package in storybook. However, I encountered an error when bundling the package with rollup, referenc ...

Alphabetically sorting objects in an array using Angular

If your TypeScript code looks something like this: items: { size: number, name: string }[] = []; ngOnInit(): void { this.items = [ { size: 3, name: 'Richard' }, { size: 17, name: 'Alex' }, ...

A guide on how to implement promise return in redux actions for react native applications

I'm using redux to handle location data and I need to retrieve it when necessary. Once the location is saved to the state in redux, I want to return a promise because I require that data for my screen. Here are my actions, reducers, store setup, and ...

What could be causing TypeORM to create an additional column in the query

Why does this TypeORM query produce the following result? const result6 = await getConnection() .createQueryBuilder() .select('actor.name') .from(Actor,'actor') .innerJoin('actor.castings',&apos ...

Creating a gradient background with the makeStyles function

Why is it that the background: 'linear-gradient(to right, blue.200, blue.700)' doesn't work within makeStyles? I just want to add a gradient background to the entire area. Do you think <Container className={classes.root}> should be rep ...

Challenges arise when incorporating interfaces within a class structure

I created two interfaces outside of a class and then proceeded to implement them. However, when I tried to assign them to private properties of the class, something went wrong and I'm unable to pinpoint the issue. Can anyone offer assistance with thi ...