"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

How to Generate a Unique URL in Angular 7 Using Typescript

I'm struggling to display or download a .pdf file in my Angular 7 project due to issues with window.URL.createObjectURL. Here's the code snippet I've written: this.userService.getFile(report.id).subscribe( res => { console.log(res) ...

Using Angular, a function can be called recursively directly from within the HTML

I'm struggling with loading an image because the method getUserProfileImage() is getting triggered multiple times within a loop. Is there a way to ensure the method is only called once during each iteration? I understand that this issue is related to ...

Converting UK DateTime to GMT time using Angular

I am currently working on an angular project that involves displaying the start and end times of office hours in a table. For instance, the office operates from 8:30 AM to 5:30 PM. This particular office has branches located in the UK and India. Since u ...

Make sure the auto import feature in TypeScript Visual Studio Code Editor is set to always use the ".js" extension

At times, the auto-import completion feature includes the .js extension, but inconsistently. When this extension is missing in the TypeScript source, the emitted JavaScript file may encounter runtime issues like module not found error since the tsc compile ...

How to dynamically set a background image using Ionic's ngStyle

I've been trying to set a background image on my card using ngStyle Take a look at my code below: <ion-slides slidesPerView="1" centeredSlides (ionSlideWillChange)= "slideChange($event)" [ngStyle]= "{'background-image': 'ur ...

TypeScript operates under the assumption that every key will be present on a Record object

Check out this code snippet: declare const foo: Record<string, number> const x = foo['some-key'] TypeScript indicates that x is of type number. It would be more accurate to say x is of type number | undefined, as there is no guarantee th ...

Best practices for updating the value of a specific key within an object that contains recursion in JavaScript/TypeScript

I have a tree component that uses the following data structure type TreeNode = { id: string, parentId: string, renderer: () => React.ReactNode, expanded: boolean, children?: Array<TreeNode>, } Now, I am looking to add functionality for ...

What is the process for importing a JSON5 file in Typescript, just like you would with a regular JSON file?

I am looking to import a JSON5 file into a JavaScript object similar to how one can import a JSON file using [import config from '../config.json']. When hovering over, this message is displayed but it's clearly visible. Cannot find module & ...

Determine whether something has the potential to be a string in TypeScript

I am looking to create a TypeScript type that can identify whether an element has the potential to be a string. This means the element should have the type "string" or "any", but not "number", "boolean", "number[]", "Person", etc. I have experimented wit ...

Incorporating node packages into your typescript projects

I have been exploring various discussions on this forum but I am still unable to make it work. My goal is to compile the following code in TypeScript. The code is sourced from a single JavaScript file, however, due to issues with module inclusion, I am foc ...

Can PassportLocalDocument and PaginateModel coexist within the same framework?

I am new to TypeScript and NestJS, looking to implement a pagination feature for all models in my application. Currently using NestJS with Mongoose for the API project. Here is an example of the user schema: export const UserSchema = new mongoose.Schema( ...

Potential 'undefined' object detected in Vuex mutation using TypeScript

Currently, I am diving into learning Vue.js alongside Vuex and TypeScript. While working on my application, I encountered an error stating "Object is possibly 'undefined'" within the Vuex Store. The error specifically arises in the "newCard" mut ...

Problem encountered during NextJS build: ReferenceError - 'window' is undefined

While I am in the process of developing my app, I have encountered a perplexing issue with a ReferenceError: window is not defined. This error seems to be happening even though I am utilizing 'use client' "use client"; import React, { u ...

Bidirectional data binding in Angular 2 allows for communication between parent components and directives

Update: Experimenting with Angular2 Beta, I am working on incorporating an "editor" component template that includes a directive wrapping the Ace editor. In this scenario, the "editor" component acts as the parent of the Ace wrapper directive, and my goal ...

Looking for guidance on converting JS code to TypeScript? Let's tackle this TS test together!

I am facing the challenge of encapsulating a very complex SDK into a layer of code. I have attempted to utilize union and index types for this task, and below is a demo that I have created. How can I implement the bar method in TypeScript to pass the conso ...

Tips for ensuring your controls function properly and seamlessly when switching to another page

I utilized the instructions from this post to implement a slider. However, I encountered an issue with the controller when navigating to subsequent pages. While the controller functions correctly on the initial page, it duplicates the same values on the fo ...

Error: Unable to locate the type definition file for the '@babel' package

I am currently working on a new project and here is the content of my package.json file. { "name": "dapp-boilerplate", "version": "1.0.0", "main": "index.js", "license": "MI ...

Angular - Monitoring Changes in Variables

I've recently started working with Angular, primarily using VueJS. I'm curious about how to detect when a variable is updated. In my case, the variable is being updated through a DataService. I came across ngOnChanges(), but it seems that it only ...

Angular is used to send an HTTP GET request

I'm seeking assistance with implementing a get and put request in Angular. I understand how to initiate a get or put request when a button is clicked, by binding the request to the button itself. However, I am now looking for a way to trigger a get re ...

Error: TypeScript React SFC encountering issues with children props typing

I am currently working with a stateless functional component that is defined as follows: import { SFC } from "react"; type ProfileTabContentProps = { selected: boolean; }; const ProfileTabContent: SFC<ProfileTabContentProps> = ({ selected, child ...