typescript - Hur man typifierar en rekursiv snittmängd, med refaktorering i åtanke?


Jag försöker omstrukturera följande kodstycke så att jag kan återanvända samma middleware-logik på flera sidor på ett typ-säkert sätt. Dock har jag lite svårt att skriva en typ-säker rekursiv typ som fungerar för följande användningsfall.

Arbetande originalkod:

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 }>['props']
>;

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;
};

Lekplatskod

I mitt första försök att skriva en rekursiv korsning mellan mellanskikt kom jag fram till denna trasiga kod:

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>>;

Hur ska jag refaktorera den här koden och skriva den här typen?

Svar #1

Jag har gjort en typimplementering som för närvarande ignorerar implementeringen av den faktiska metoden och bara summerar de egenskaper som passerats genom typningen

props-parametern är för närvarande typad som ett tomt objekt, vilket skulle vara mycket svårt att typa korrekt eftersom "main"-objektet kommer från det sista anropet

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 >;

på andra tanken gör det egentligen ingen mening att skriva props, för inuti props-implementeringen kan du inte veta i vilken ordning de är inbäddade i varandra

länk till lekplats med komplett lösning

Relaterade artiklar