Issue with Typescript: Generic type is not accurately inferred as `unknown`

Is anyone familiar with creating a system similar to Redux/Redux-toolkit? What I'm attempting to do is implement a createSlice function like the one below:

interface Lobby {
  players: string[];
}

const slice = createSlice<Lobby>({
  addPlayer: (state, name: string) => ({ ...state, players: [...players, name ] }),
});

The expected output would be:

{
  actions: { addPlayer: (name: string) => ({ type: 'addPlayer', payload: name }) }
  reducer: (state: Lobby, action: { type: string, payload: any }) => Lobby
}

However, it seems that it's not possible to achieve this due to multiple generic arguments in createSlice, and being unable to only partially apply state as a parameter.

The best solution so far is something along the lines of:

const createSlice = <S, T extends Record<string, (state: S, payload: any) => S>>(
  actions: T
) => {
  type ActionsType = {
    [K in keyof T]: (
      payload: Parameters<T[K]>[1]
    ) => { type: K; payload: Parameters<T[K]>[1] };
  };

  type ReducerType = (state: S, { type: string, payload: any }) => S;

  return {
    actions: ("implementation" as any) as ActionsType,
    reducer: ("implementation" as any) as ReducerType,
  };
};

This implementation can be used as shown below:

const slice = createSlice({
  addPlayer: (state: Lobby, name: string) => ({ ...state, players: [...players, name ] }),
});

Although this works, there is an issue with the inferred type of parameter S being considered as unknown, resulting in a wrong reducer type. For example, the following scenario does not trigger a type error:

const actions = slice({
  addPlayer: (state: Lobby, name: string) => 3, // This should cause a type error
});

At the moment, I am unsure how to proceed or rectify this issue..

Answer №1

I am still trying to fully grasp the root cause of the initial issue, but I found that isolating generic types helped in resolving errors within the codebase. By allowing createSlice to accept a single generic argument, it seems to address the errors effectively.

type ActionOptions<S> = Record<string, (state: S, payload: any) => S>;
type ActionsType<S, T extends ActionOptions<S>> = {
    [K in keyof T]: (
      payload: Parameters<T[K]>[1]
    ) => { type: K; payload: Parameters<T[K]>[1] };
};

type ReducerType<S> = (state: S, {type, payload}: { type: string, payload: any }) => S;

function createSlice<S> (
  actions: ActionOptions<S>
) {  
  return {
    actions: ("implementation" as any) as ActionsType<S, ActionOptions<S>>,
    reducer: ("implementation" as any) as ReducerType<S>,
  };
};

type Lobby = { __brand: 'lobby' };

const slice = createSlice({
  addPlayer: (state: Lobby, payload: string) => ({ ...state, players: [payload ] }),
});

const actions = createSlice({
  addPlayer: (state: Lobby, name: string) => 3, // Type 'number' is not assignable to type 'Lobby'
});

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

Cannot assign Angular 4 RequestOptions object to post method parameter

I'm having trouble with these codes. Initially, I created a header using the code block below: headers.append("Authorization", btoa(username + ":" + password)); var requestOptions = new RequestOptions({ headers: headers }); However, when I tried to ...

Is it possible to execute TestCafe tests using TypeScript page objects that have not been utilized?

While working with TestCafe, I am implementing tests using the Page Objects pattern. I have already written some page objects in advance, even before their actual usage, as I am familiar with the page and know what to anticipate. However, when attempting ...

Unable to locate identifiers 'Let' (TS2304), 'headers' (TS2552), and 'options' in a TypeScript script

I am new to using Angular and Ionic. While following a tutorial, I encountered the following errors: Cannot find name ‘Let’ (TS2304) Cannot find name ‘headers’. Did you mean ‘Headers’? (TS2552) Cannot find name ‘options’. Did you mean ‘ ...

What is the method for obtaining the number of weeks since the epoch? Is it possible to

Currently, I am setting up a DynamoDb store for weekly reporting. My idea is to use the week number since 1970 as a unique identifier for each report record, similar to epoch milliseconds. Here are some questions I have: How can I determine the current w ...

Testing the mirkoORM entities at a unit level

Trying to perform a unit test on a method within a MikroORM entity, I am attempting to populate a mikroORM collection field with test data. Specifically, I am using jest for this task: describe('Team Tests', () => { it('isLeader shoul ...

The microservices system fails to initialize

I've recently delved into the world of microservices, but I've hit a roadblock in my application. app.listen(port) Despite adding .catch() I'm still unable to figure out what's going wrong. The function in question looks like this: nx ...

Declaratively assign ambient typings to an unfamiliar object in Typescript

I am facing an issue with an external library named "gapi" that is set to a property on the window object as window.gapi. I would like to keep it there while using the @types/gapi declaration. Is there a way to achieve this, like the following code snippet ...

`Switching from Fetch to Axios: A step-by-step guide`

Currently in the process of refactoring some code and need to transition from using fetch to axios. Here's the original code snippet: const createAttachment = async (formData: FormData): Promise<boolean | string> => { try { const respon ...

Ways to optimize TypeScript's array type inference algorithms

My dilemma revolves around defining a type/interface for a collection of properties where each property has a specified type. You can reference Props in the code snippet below for clarification. The goal is to create a type that represents a tuple <A, ...

Can you provide guidance on effectively utilizing a Pinia store with Vue3, Pinia, and Typescript?

I'm currently facing challenges while using the Pinia store with TypeScript and implementing the store within a basic app.vue Vuejs3 option api. Here is my app.js file: import {createApp} from 'vue' import {createPinia} from "pinia&quo ...

The IntrinsicAttributes type does not include the property 'path' in the Preact router

I am facing a challenge while developing my website using preact router. Every time I try to add something to the router, I encounter an error stating "Property 'path' does not exist on type 'IntrinsicAttributes'." Despite this error, t ...

What are the steps to send a Firebase cloud message using typescript?

I'm looking for guidance on how to send a Firebase Cloud Message through my Firebase Functions backend. I seem to be running into trouble with the payload and need help resolving it. Do I need an interface for the payload? Thank you in advance! Error ...

Understanding the event type for event listener callback function in React and Typescript

I am attempting to manage the scroll event in order to add a new class name to an element when scrolling occurs. Here is what I have accomplished: Created a callback function for the event listener Utilized useEffect and addEventListener Below is the co ...

Mastering the art of accessing properties in typescript post implementing Object.defineProperty

I was experimenting with the TypeScript playground trying to figure out decorators and encountered some questions. class PathInfo { functionName: string; httpPath: string; httpMethod: string; constructor(functionName: string, httpPath: str ...

Exploring Angular 6: Searching for intricate objects within a list and extracting names from a reference table

I am looking to search a list of complex objects. Here is an example of how my list is structured: private employees: Employee[] = [ { name: 'Mary Jay', departmentsList: [1, 2, 3], //these are the IDs of departments from the l ...

The optimization efforts on several components ended up having a negative impact on the overall performance

I've been dedicated to optimizing an online game app recently. This React app has a large code base and is experiencing some major lag issues, especially on the mobile version. Throughout this process, I've come across various challenges includi ...

Tips for generating a hyperlink in a Typescript file using Angular version 16 and above

I am encountering an issue with my consts.ts file in the project. Specifically, I have defined a constant LINK1 as <a href='https://sample.com/'>LINK 1</a>; However, this setup is not working as expected. What I actually want is to d ...

Typescript integration in Next.js

When deploying a Next.js project with TypeScript, sometimes errors occur that do not show up during development. Here is an example of such an error: 13:09:26 Failed to compile. 13:09:26 ./node_modules/next/dist/build/webpack/plugins/pages-manifest ...

When attempting to set a variable type using ":URL", an error is generated

When I created a file named "test.ts" in VSCode, I included the following code: var data = "https://a.b.c/d/e/f"; var url = new URL(data); console.log(url.protocol); After pressing F5, the output was "https:". However, when I added a type declaration like ...

Creating a new object with inline syntax for an interface in Typescript

Many claim that you can create an object using interface in the following manner: let obj: ILookup = { id: 1, name: 'abc'} Could you please provide me with the syntax to write this inline, for example like so: lookups.push(ILookup = { id: 1, nam ...