What in the world is going on with this Typescript Mapped type without a right-hand side?

I encountered a situation where my React component had numerous methods for toggling boolean state properties. Since these functions all did the same thing, I wanted to streamline the process by creating a common function for toggling properties.

Each method followed this structure:

toggleProperty() {
    this.setState(previous => ({
        myProperty: !previous.myProperty
    }))
}

To simplify things, I devised a universal method that could be invoked with the relevant context:


/** Filters out keys of B that doesn't have the same type of T **/

type FilterOutUnmatchedType<B extends Object, T extends any> = {
    [K in keyof B]: B[K] extends T ? K : never;
}[keyof B];


private StateToggler(this: Configurator, property: FilterOutUnmatchedType<ConfiguratorState, boolean>) {
        this.setState((previous) => ({
            [property]: !previous[property]
        });
    }

In this method, my goal was to only accept boolean properties from the state object. While FilterOutUnmatchedType served its purpose, I encountered an error in Visual Studio Code:

Argument of type '(previous: Readonly<ConfiguratorState>) => { [x: string]: boolean; }' is not assignable to parameter of type 'ConfiguratorState | ((prevState: Readonly<ConfiguratorState>, props: Readonly<ConfiguratorProps>) => ConfiguratorState | Pick<...>) | Pick<...>'.
  Type '(previous: Readonly<ConfiguratorState>) => { [x: string]: boolean; }' is not assignable to type '(prevState: Readonly<ConfiguratorState>, props: Readonly<ConfiguratorProps>) => ConfiguratorState | Pick<...>'.
    Type '{ [x: string]: boolean; }' is not assignable to type 'ConfiguratorState | Pick<ConfiguratorState, (omitted, keyof ConfiguratorState - basically))>'

It appeared that the code was considering [property]: boolean too generic, even though property was within keyof ConfiguratorState.

After multiple attempts to resolve this issue, I stumbled upon a solution that seemed to work without clear explanation. The syntax involved a casting as part of Mapped Types:

this.setState(
    (previous) => ({
        [property]: !previous[property]
    } as { [K in keyof ConfiguratorState] })
);

This usage of Mapped Types caught me off guard, as I initially expected a right-hand side assignment. Surprisingly, omitting the RHS like ConfiguratorState[K] resulted in the desired outcome. Though perplexing, the code executed correctly.

If anyone can shed light on this concept and direct me to additional resources, I would greatly appreciate it!

Answer №1

In cases where a right hand side is not provided, the type is automatically assumed to be any. Therefore,

{ [K in keyof ConfiguratorState] }
can be considered as
{[K in keyof ConfiguratorState]: any}
, or
Record<ConfiguratorState, any>
. It's important to note that when noImplicitAny is enabled, TypeScript will generate an error due to the implied any type.

Check out this Playground link for demonstration.

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

When employing `queryParams` in Angular routing, the URL will automatically replace `%` with `%25`

When trying to use queryParams for navigation in Angular routing, I encountered an issue. <a routerLink='/master' [queryParams]="{query:'%US',mode:'text'}"><li (click)="search()">Search</li></a> The d ...

TypeScript's type casting will fail if one mandatory interface property is missing while an additional property is present

While running tsc locally on an example file named example.ts, I encountered some unexpected behavior. In particular, when I created the object onePropMissing and omitted the property c which is not optional according to the interface definition, I did not ...

Error: Module 'react' not found. Please make sure it is installed and correctly imported

Recently, I've been working on developing a React app using TypeScript. To kickstart the project, I used yarn create react-app (name) --use-pnp --typescript. However, I encountered an issue with the TS linter repeatedly showing the error: Cannot find ...

Typescript displays an error message when attempting to assign a list of string variants to a defined type

Encountering an interesting TS error in the code snippet below: interface Bar { pictureType: "X" | "Y" } interface RT { output: Bar[] } const func = (): RT => { const list = [{ pictureType: 'X', }] r ...

Transform the date format from Google Forms to TypeScript

I am currently facing an issue with a Google Form connected to a Google Spreadsheet. The date format in the spreadsheet appears as follows when a response is received: 20/02/2023 18:58:59 I am seeking guidance on how to convert this date format using Type ...

Error encountered in Ngrx data service when using Angular resolver

I have successfully implemented an NGRX Data entity service and now I'm looking to preload data before accessing a route by using a resolver. import { Injectable } from "@angular/core"; import { ActivatedRouteSnapshot, Resolve, RouterS ...

Do not display large numbers within an HTML card

I have https://i.sstatic.net/DkowD.png this card here and displaying dynamic data inside it. The number is quite large, so I would like it to appear as 0.600000+. If a user hovers over the number, a tooltip should display the full number. How can I achieve ...

Operator in RxJS that maps the elements within an array composed of multiple arrays

disclaimer: I have a creative solution and would like to share it here. While exploring the RxJS documentation for map methods that meet this specific requirement - let's call it mapArray - I haven't been able to find one. of([1, 2, 3]).pipe( ...

Attempting to declare a Typescript function with the 'any' type will result in encountering the 'No ideal common type' error regardless

I am struggling with a recurring "No best common type" error, even though I have assigned the function a 'any' type. I have also experimented with combinations of types like 'any|string', 'string|any'. . . Any help would be gr ...

The 'authorization' property is not available on the 'Request' object

Here is a code snippet to consider: setContext(async (req, { headers }) => { const token = await getToken(config.resources.gatewayApi.scopes) const completeHeader = { headers: { ...headers, authorization: token ...

Choose from the Angular enum options

I am working with an enum called LogLevel that looks like this: export enum LogLevel { DEBUG = 'DEBUG', INFO = 'INFO', WARNING = 'WARNING', ERROR = 'ERROR' } My goal is to create a dropdown select el ...

Issue with debugging Azure Functions TypeScript using f5 functionality is unresolved

I am encountering issues running my Azure TypeScript function locally in VS code. I am receiving the errors shown in the following image. Can someone please assist me with this? https://i.stack.imgur.com/s3xxG.png ...

Troubleshooting the inclusion of nodemon in package.json

I am interested in implementing nodemon to automatically recompile the project when there are changes made to the code during development. package.json { "name": "insurance-web-site", "version": "0.1.0", " ...

What is the correct way to utilize Global Variables in programming?

Having trouble incrementing the current page in my pagination script to call the next page via AJAX... In my TypeScript file, I declare a global variable like this; declare var getCurrentPage: number; Later in the same file, I set the value for getCurren ...

Is it considered best practice to include try/catch blocks within the subscribe function in RxJs?

Imagine we have an Angular 2 application. There is a service method that returns data using post(), with a catch() statement to handle any errors. In the component, we are subscribing to the Observable's data: .subscribe( ()=> { ...

Creating dynamic keys to insert objects

In my coding project, I am dealing with an empty array, a value, and an object. Since there are multiple objects involved, I want to organize them into categories. Here is an example of what I envision: ARRAY KEY OBJECT OBJECT KEY OBJECT ...

Customizing default attribute prop types of HTML input element in Typescript React

I am currently working on creating a customized "Input" component where I want to extend the default HTML input attributes (props). My goal is to override the default 'size' attribute by utilizing Typescript's Omit within my own interface d ...

Consolidate multiple generic items into a single entry

In my current project, I am structuring the types for a complex javascript module. One of the requirements is to handle multiple types using generics, as shown in the snippet below: export interface ModelState< FetchListPayload, FetchListR ...

Having trouble showing table data in Angular

My goal is to showcase data from a table created using Spring-Boot Below is my model.ts: export class Quiz1 { QuestionId?: any; Question?: string; OptionsA?: string; OptionsB?: string; OptionsC?: string; OptionsD?: string;} He ...

Is it possible to dynamically name keys in objects using template literals in Typescript?

Can the scenario below be achieved? type test = <T extends string>(key: T, object: { [`${T}`]: number }) => void ^^^^^^^^ I am aware that we can assign type literal values using that syntax, but af ...