Assign the initial value to the first parameter of a TypeScript function by referencing the second optional parameter

In the process of developing a state manager library for React, I came up with an idea to implement a helper function that would allow users to easily set deeply nested states from outside of React using a selector. The helper function I have currently looks something like this:

type NestedStateSetter<State> = <Selected = State>(
    update: Selected | (() => Selected) | (() => Promise<Selected>),
    selector?: (state: State) => Selected
) => Promise<Selected>;

const initialState = {
    a: {
        b: {
            c: 0,
        },
    },
};

const setState: NestedStateSetter<typeof initialState> = (update, selector) => {
    throw "Yeet"
};

setState(1, (state) => state.a.b.c) // Ok
setState(() => 2, (state) => state.a.b.c) // Ok
setState(async () => 5, (state) => state.a.b.c) // Not ok

However, when trying to use 'setState' with async function in the third call, it leads to an error related to type mismatch on the (state) => state.a.b.c parameter:

Argument of type '(state: { a: { b: { c: number; }; }; }) => number' is not assignable to parameter of type '(state: { a: { b: { c: number; }; }; }) => Promise'. Type 'number' is not assignable to type 'Promise'.(2345)

TypeScript Playground link
Stackblitz link

I considered implementing a curried function to address this issue, but that might introduce breaking changes which I want to avoid if possible.

Answer №1

To ensure TypeScript does not match the first, most generic type in your union, it is important to order the types from most specific to least specific.

Instead of:

update: Selected | (() => Selected) | (() => Promise<Selected>)

Consider using:

update: (() => Promise<Selected>) | (() => Selected) | Selected

Complete example:

type NestedStateSetter<State> = <Selected = State>(
  update: (() => Promise<Selected>) | (() => Selected) | Selected,
  selector?: (state: State) => Selected
) => Promise<Selected>;

const initialState = {
  a: {
    b: {
      c: 0,
    },
  },
};

const setState: NestedStateSetter<typeof initialState> = (update, selector) => {
  throw 'Yeet';
};

setState(1, (state) => state.a.b.c); // Ok
setState(
  () => 2,
  (state) => state.a.b.c
); // Ok
setState(
  async () => 5,
  (state) => state.a.b.c
); // Ok

Try it on 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

The unit test is running successfully on the local environment, but it is failing on Jenkins with the error code TS2339, stating that the property 'toBeTruthy' is not recognized on the type 'Assertion'

I've been tackling a project in Angular and recently encountered an issue. Running 'npm run test' locally shows that my tests are passing without any problems. it('should create', () => { expect(component).toBeTruthy();}); How ...

Storing a reference globally in React and Typescript: Best practices

In my application, I have multiple instances of a specific component called <Item>. Each <Item> needs to display a dynamic tooltip when hovered over. To achieve this, I am utilizing semantic-ui-react and its Popup component. The conventional m ...

Cypress automation script fails to trigger Knockout computed subscription

Within my setup, I have implemented two textboxes and a span to display the result. Date: <input data-bind="value: dateValue"/> Number: <input data-bind="value: dateValue"/> Result : <span data-bind="text: calculatedValue">Result Should ...

The customized theme for Material UI version 5.10.9 is reflected in the Box component

, As of late, I've been utilizing Material UI version 5 in React. When creating a custom theme and incorporating my own palette, I observed that the Box component adheres to the default Material UI theme and disregards my customized theme. Curiously, ...

The program encountered a problem stating that the 'getItem' property is not found within the 'string' type

I am utilizing Firebase to register links on a website. I'm attempting to pass the uuid in order to create a sub collection, but have been unsuccessful. Any idea what might be causing this issue? constructor(private af: AngularFirestore) {} async add ...

Discovering the power of Angular 2 with ngrx while putting my Reducer to the

Within my Reducer file: case PumpActionTypes.EnterLocalMode: return commandOne.upsertOne( {id: action.payload.id, changes: { local: false }}, state ); When testing, I aim to verify that the local property is indeed modified to false. My curr ...

What is the best way to retrieve a property from a conditional type using generics?

The code snippet above presents an issue in TypeScript: const exampleFn = function<AttributeName extends 'attributeA' | 'attributeB'>( whatToProcess: AttributeName extends 'attributeA' ? {attributeA: string} : {attri ...

Is it possible to find a more efficient approach than calling setState just once within useEffect?

In my react application, I find myself using this particular pattern frequently: export default function Profile() { const [username, setUsername] = React.useState<string | null>(null); React.useEffect(()=>{ fetch(`/api/userprofil ...

I'm looking to incorporate a module from another component (Next.js, React.js) into my project

I need to implement the "StyledSwiperPagination(swiper-pagination-bullet) at SwiperImages.tsx" in "index.tsx". The problem is that when I added <StyledSwiperPagination /> in index.tsx, nothing appeared on the screen. Lorem ipsum dolor sit amet, co ...

Tips for incorporating the return value of a function into my macro design

To avoid falling into the trap of an X-Y problem, I want to explain my intention upfront. I have numerous self-defined structs, all functioning as static singleton variables. My goal is to create a macro (or function) that will retrieve the specific addres ...

Experiencing issues with Errors when Targeting ES5 in Angular2 TypeScript?

In my development environment, the npm version is 3.10.10, and I want to create a new Angular2 project from scratch. When I try running npm install angular2 --save I encounter this error message: Error Image After referring to this answer which recomm ...

Displaying Firebase values in an Angular 2 list is a breeze

Here is the functionality to load, add, and mark ToDo as Finished: todos: FirebaseListObservable<any>; ngOnInit(){ this.todos = this._af.database.list('todos') } addTodo(newTodo: string){ this.todos.push({ ...

Utilize a single component across various instances for enhanced efficiency

After thorough research, I couldn't find a solution to my problem despite similar questions being asked. I've developed an angular component for styled radio buttons and need to use it multiple times on different instances. To get a better unde ...

What is the best way to connect my Typescript NextJS code to my Express API?

My goal is to extract data from my API, which is providing the following JSON: [ { project: "Challenges_jschallenger.com" }, { project: "Using-Studio-Ghilis-API-With-JS-Only" }, { project: "my-portfolio-next" }, { proj ...

How to pass a dynamically allocated 3D array to a function in C++?

After dynamically allocating a 3D array and assigning strings into it, I encountered an issue when trying to pass the array to a function. Despite my efforts with different approaches, I couldn't find a successful way to pass the array. Below is the c ...

Angular5/Karma is raising the error message "selector is not a recognized element"

I am encountering a small issue with my component. While everything seems to be working fine in my browser without any errors, the Karma debugger is throwing some errors that I would like to resolve for clarity. Error: Failed: Template parse errors: &apo ...

Problems Encountered When Converting a Function to TypeScript

I've been trying to convert this function to TypeScript, but I've encountered some issues: import { Children, ReactNode } from 'react'; interface ChildType { role?: string; } export default function filterChildrenByRole( children: ...

What is the best way to incorporate this in a callback function?

Utilizing a third-party component requires creating an object for configuration, such as itemMovementOptions in the given code sample. export class AppComponent implements OnInit { readonly itemMovementOptions = { threshold: { horizontal: ...

Tips for choosing the node_modules distribution flavor to include in your webpack bundle

Issue: Following the update of AJV.js to Version 6.4, my vendor bundle now includes the "uri-js" ESNEXT version instead of the ES5 version, causing compatibility issues with IE11. Analysis: Upon investigation, I discovered that AJV references uri-js usi ...

Deselect the checkbox that has been selected using a JavaScript function

I have been searching everywhere for a solution and I am hoping someone in this community can assist me. I am working with a script that triggers when a checkbox is selected from a group of checkboxes. Each checkbox is given a value of "customer id". < ...