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
