What is the best approach to implement a recursive intersection while keeping refactoring in consideration?

I'm currently in the process of refactoring this code snippet to allow for the reuse of the same middleware logic on multiple pages in a type-safe manner. However, I am encountering difficulties when trying to write a typesafe recursive type that can cater to the specific use case at hand.

Here is the working original code:

import { NextPage, GetServerSidePropsContext } from 'next';

// new InferGetServerSidePropsType fix, waiting for merge to stable
type InferGetServerSidePropsType<T extends (args: any) => any> = Awaited<
  Extract<Awaited<ReturnType<T>>, { props: any }>
>;

const getServerSideProps = async (context: GetServerSidePropsContext) =>
  (async (context, props) => {
    const token = 'token';
    if (Math.random() > 0.5)
      return {
        notFound: true,
      };
    return (async (context, props) => {
      if (context.locale === 'en')
        return {
          redirect: {
            destination: '/en',
            permanent: true,
          },
        };
      const permissions = [1, 2, 3];
      return (async (context, props) => {
        const data = 'data';
        return { props: { ...props, data } };
      })(context, { ...props, permissions });
    })(context, { ...props, token });
  })(context, {});

const MyPage: NextPage<InferGetServerSidePropsType<typeof getServerSideProps>> = (props) => {
  const { token, permissions, data } = props; // types are infered correctly!
  return null;
};

Playground code

During my initial attempt at defining a recursive intersection between middlewares, I ended up with the following flawed code:


const withToken: GSSPMiddleware<{ token: string }> = (next) => async (context, props) => {
  if (Math.random() > 0.5)
    return {
      notFound: true,
    };
  const token = 'token';
  return next(context, { ...props, token });
};

const withPermissions: GSSPMiddleware<{ permissions: number[]}> = (next) => async (context, props) => {
  if (context.locale === 'en')
    return {
      redirect: {
        destination: '/en',
        permanent: true,
      },
    };
  const permissions = [1, 2, 3];
  return next(context, { ...props, permissions });
};

const getServerSideProps = async (context: GetServerSidePropsContext) =>
  withToken(
    withPermissions(async (context, props) => { // props: {token: string} & {permissions: number[]}
      const data = "data";
      return { props: { ...props, data } };
    })
  )(context, {});

const MyPage: NextPage<InferGetServerSidePropsType<typeof getServerSideProps>> = (props) => {
  const { token, permissions, data } = props; // types should infer correctly!
  return null;
};

// My attempt, completely wrong
type GSSPMiddleware<Params extends { [key: string]: any } | undefined = undefined> = <
  P extends { [key: string]: any } = { [key: string]: any },
>(
  next: (
    context: GetServerSidePropsContext,
    props: Params extends undefined ? P : P & Params
  ) => Promise<GetServerSidePropsResult<Params extends undefined ? P : P & Params>>
) => (
  context: GetServerSidePropsContext,
  props: P
) => Promise<GetServerSidePropsResult<Params extends undefined ? P : P & Params>>;

How can I effectively refactor this code and define the appropriate type?

Answer №1

Currently, I have developed a type implementation that disregards the actual method's implementation and simply aggregates the properties passed through typing.

The props parameter is currently typed as an empty object, which can be quite challenging to type correctly since the "main" object comes from the last call.

type GSSPMiddleware<AddingParams extends { [key: string]: any } = {}> = <
  NextFnc extends <T>(context:GetServerSidePropsContext,props:{})=>any,
  CustomProps extends {} = {}
>(
  next: NextFnc
)  => <ParentProps>(context:GetServerSidePropsContext,props:ParentProps & CustomProps)=>Promise<{
  props:AddingParams 
  } & (Awaited<ReturnType<NextFnc>> extends {props:infer Pr}?{props:Pr}:{}) & ParentProps >;

Upon further consideration, typing the props doesn't seem logical because within the props implementation, you cannot determine the order in which they are nested inside each other.

Check out the playground for a comprehensive solution.

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

An instance of an object is being added instead of parameters

I'm having some trouble making a server call using promises. Whenever I try to add my parameters, they end up showing as 'object%20Object' Here's the code snippet for the call: import { Injectable } from '@angular/core'; imp ...

How to automatically disable a button in reactjs when the input field is blank

I have a component called Dynamic Form that includes input fields. The challenge I am facing is how to disable the submit button when these input fields are empty, although the validateResult function fails to return false. import cn from "classname ...

A step-by-step guide on integrating Detox with jest (ts-jest) and Typescript in a react-native app

I've been experimenting with incorporating Typescript into my detox tests. The most relevant information I could find was in this gist. However, when trying to implement it, I encountered an error stating that jasmine is not defined. After researching ...

Ways to avoid the unmounting and remounting of components in Next.js

I am working on a Next.js 14 app that includes a Chat component which I want to prevent from unmounting/remounting. The problem arises when I have added buttons on the mobile screen to expand and close the chat. Every time I expand or close the Chat, the c ...

To enable iteration of iterators when trying to spread, utilize the TypeScript compiler option '--downlevelIteration'

I have a function that accepts an argument of either an object or an array. const handleScenarioChange = (scenario: Scenario | Scenario[]) => { if (isArray(scenario)) { const scenarios = [...state.selectedScenarios, ...scenario]; const unique ...

A Typescript type that verifies whether a provided key, when used in an object, resolves to an array

I have a theoretical question regarding creating an input type that checks if a specific enum key, when passed as a key to an object, resolves to an array. Allow me to illustrate this with an example: enum FormKeys { x = "x", y = "y&q ...

What is causing the issue with using transition(myComponent) in this React 18 application?

Recently, I have been immersed in developing a Single Page Application using the latest version of React 18 and integrating it with The Movie Database (TMDB) API. My current focus is on enhancing user experience by incorporating smooth transitions between ...

The functionality of the KendoReact Grid for filtering and sorting is not functioning correctly when data is grouped

Once I group the data, the filter and sort functions in the KendoReact Grid stop working. Interestingly, if I bypass the grouping step and show the data without grouping, the filter and sort functions function perfectly fine. My main objective is to figu ...

What is the best way to automatically refresh an observable every 30 seconds?

@Component({ selector: 'app-geo', templateUrl: <img mat-card-image [src]="profileUrl | async "> export class GeoComponent implements OnInit { date; profileUrl: Observable<string>; constructor(private tempService ...

What could be the reason for `process.env.myvariable` not functioning in a Next.js project with TypeScript

After creating a file called .env.local in the root directory of my project, I added a variable called WEBSOCKET_VARIABLE=THIS_IS_TEXT to it. However, when I try to access it using process.env.WEBSOCKET_VARIABLE, nothing is found. What could be causing ...

Guidance on deploying a Next JS application to Azure App Service using Azure Pipeline

Exploring the world of Next.JS, I am currently delving into deploying it to an Azure App Service through a pipeline in Azure. At this initial stage, my project consists of a basic create-next-app as its foundation. Within my pipeline, I have successfully e ...

What is the best way to store a logged-in user's email in a React

I have implemented a login API and I would like to save the email of the logged-in user in the state or another variable, so that I can display it in the header after successful login. The user's email is located in the data.email. The following code ...

Tips on executing an asynchronous operation before exiting

I have been attempting to execute an asynchronous operation before my process ends. By 'ends', I mean in every instance of termination: ctrl+c Uncaught exception Crashes End of code Anything.. As far as I know, the exit event handles this for ...

What is the best way to sort through an Array of photo filenames?

I have a list of image names that contain UUIDs. images [ "auditid_626_UUID_666666_time__1582577405550.jpg", "auditid_626_UUID_999999_time__1582577405554.jpg", "auditid_626_UUID_999999_time__1582577405557.jpg" ] These ima ...

Implementing debounce in Subject within rxjs/Angular2

I am currently building an events service, and here is the code snippet I am using: import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; import { Subject } from 'rxjs/Subject'; export int ...

angular2 fullCalendar height based on parent element

Currently, I am using angular2-fullcalendar and encountering an issue with setting the height to 'parent'. The parent element is a div but unfortunately, it does not work as expected. The navigation bar appears fine, however, the calendar itself ...

What could be the reason behind my Vue file triggering a TS6504 error and suggesting to "allowJs"?

My Vue 3 project, which incorporates TypeScript, is encountering an issue when I attempt to execute vue-tsc --noEmit. error TS6504: File '/proj/src/pages/Index.vue.__VLS_template.jsx' is a JavaScript file. Did you mean to enable the 'allowJs ...

Issue with S3 when attempting to download an image due to CORS

I am encountering an issue with downloading images from S3. It was functioning properly recently, but now I am facing a CORS Error. My setup includes Next.js v12.3.0 and Strapi. Here is the CORS Configuration that I have implemented: (imagine that I added ...

Need assistance with the Angular polyfill.ts file? Wondering where to place the polyfill code and how to manage it effectively?

Currently encountering an error in my Angular project that requires some 'polyfilling'. Due to the restriction on editing webpack.config.js directly, it seems necessary to work with the polyfill.ts file instead. The issue lies in the fact that An ...

Using the `ngrx` library to perform an entity upsert operation with the

I am facing a certain challenge in my code. I have an action defined as follows: export const updateSuccess = createAction('Success', props<{ someId: string }>()); In the reducer, I have an adapter set up like this: export const adapter: ...