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

Issue arises when compiling React Redux due to a union type that includes undefined

I am currently in the process of learning how to integrate Redux with React using Typescript. I encountered a compilation error that relates to the type of my store's state, specifically as {posts: PostType[]}. The error message states: Type '{ p ...

When setupFilesAfterEnv is added, mock functions may not function properly in .test files

Upon including setupFilesAfterEnv in the jest.config.js like this: module.exports = { preset: 'ts-jest', testEnvironment: 'node', setupFilesAfterEnv: ["./test/setupAfterEnv.ts"] } The mock functions seem to sto ...

Why isn't the page showing up on my nextjs site?

I've encountered an issue while developing a web app using nextjs. The sign_up component in the pages directory is not rendering and shows up as a blank page. After investigating with Chrome extension, I found this warning message: Unhandled Runtime ...

How can I clear the div styling once the onDismiss handler has been triggered

Seeking assistance with resetting a div on a Modal after it has been closed. The issue I am facing with my pop-up is that the div retains the previous styling of display: none instead of reverting to display: flex. I have searched for a solution without su ...

Is there a way to determine the quantity of lines within a div using a Vue3 watcher?

Is it feasible to determine the number of text lines in a div without line breaks? I am looking to dynamically display or hide my CTA link based on whether the text is less than or equal to the -webkit-line-clamp value: SCRIPT: const isExpanded = ref(true ...

The functionality of Angular 6 Material Nested Tree is disrupted when attempting to use dynamic data

In Angular 6, I am utilizing mat-tree along with mat-nested-tree-node. My objective is to dynamically load the data when the user toggles the expand icon. Attempting to apply the dynamic data concept from the Flat Tree example provided in Material Example ...

Explain to me the process of passing functions in TypeScript

class Testing { number = 0; t3: T3; constructor() { this.t3 = new T3(this.output); } output() { console.log(this.number); } } class T3 { constructor(private output: any) { } printOutput() { ...

Incorporate a file into all API endpoints with Next.js API functionality

Is there a way to incorporate a "bootstrap" file (a file with side-effects) as the first file included in all Next.js APIs? The main issue is that I have a Winston logger in a file that needs to be added to every API endpoint, but this process hinders dev ...

Is there a way to extract slugs from the requested page using getStaticProps or getStaticPaths?

I'm looking to retrieve data for each page number individually. Here's an example: URL: export async function getStaticProps(data) { console.log(data.slugs.pagenumber)//1 } ...

Implement the click event binding using classes in Angular 2

If I have the template below, how can I use TypeScript to bind a click event by class? My goal is to retrieve attributes of the clicked element. <ul> <li id="1" class="selectModal">First</li> <li id="2" class="selectModal">Seco ...

What's the best way to integrate a Prisma object into the routing system of a NextJS 13 application

Currently using Prisma with SQLite, my User model stores id, name, and email fields. Within the file /alluser/page.tsx, there is a function implemented to fetch data from Prisma. export async function getAllUsers(){ const prisma = new PrismaClient( ...

Creating versatile list components that can accommodate various types of list items

As part of my project using Next.js, typescript, and type-graphql, I found myself creating Table components. These components are meant to display custom object types as rows within a table. While each piece of data has its own unique structure, they all ...

Traversing fields of a document within a Firestore collection using Angular

Attempts to retrieve the user's photoUrl based on their ID have been unsuccessful. Here is a snapshot of my firestore collection, can someone please guide me on how to access the photoUrl? https://i.stack.imgur.com/p2Zvm.jpg The main collection is &ap ...

Is there a way to use dot notation in TypeScript for a string data type?

I'm currently in the process of developing a function API with a specific format: createRoute('customers.view', { customerId: 1 }); // returns `/customers/1` However, I am facing challenges when it comes to typing the first argument. This ...

Leveraging FormControlName in Typescript to Interact with HTML Components in Angular 4

How can I use FormControlName to access HTML elements in typescript? Typically, I am able to access HTML elements using their ID. For example: var element = document.getElementById("txtID") But is it possible to access the element without using its ID a ...

Error: The argument provided is of type 'unknown', which cannot be assigned to a parameter of type 'string'. This issue arose when attempting to utilize JSON.parse in a TypeScript implementation

I'm currently converting this code from Node.js to TypeScript and encountering the following issue const Path:string = "../PathtoJson.json"; export class ClassName { name:string; constructor(name:string) { this.name = name; } ...

Implementing try-catch logic for element visibility in Playwright using TypeScript poses limitations

There is a specific scenario I need to automate where if the title of a page is "Sample title", then I must mark the test as successful. Otherwise, if that condition is not met, I have to interact with another element on the page and verify if the title ch ...

Having trouble assigning the current page in NextJS navigation

I have a unique setup for my navigation system in my NextJS project: const menu = [ { name: 'Home', href: '/home', icon: HomeIcon, current: true }, { name: 'About', href: '/about', icon: InfoIcon, current: fa ...

Is there a way to update components in Angular without affecting the current URL?

I want to update a component without changing the URL. For example, I have a component called register. When I visit my website at www.myweb.com, I would like to register by clicking on sign up. How can I display the register component without altering the ...

Having difficulties constructing a project that contains a personalized worker document

I've encountered a puzzling problem with next-pwa. Every time I attempt to compile a next-pwa project that involves a custom worker js file, the compilation fails with this error: info - Creating an optimized production build ..buffer.js:333 throw ...