D
Deno

help

My magic middlewares type

Eeliassjogreen2/11/2023
If anyone wants a typescript challange here is one: I am trying to create a tuple type which contains an sequence of MiddlewareHandler types. This MiddlewareHandler is defined as:
export type MiddlewareHandler<I = unknown, O extends I = I> = (
req: Request,
ctx: MiddlewareContext<I, O>,
) => Response | Promise<Response>;
export type MiddlewareHandler<I = unknown, O extends I = I> = (
req: Request,
ctx: MiddlewareContext<I, O>,
) => Response | Promise<Response>;
I have a function called compose which composes these MiddlewareHandlers. This function takes a generic array of MiddlewareHandler:
type ExtractInput<T extends readonly MiddlewareHandler[]> = T[0] extends
MiddlewareHandler<infer I> ? I : never;
type ExtractOutput<T extends readonly MiddlewareHandler[]> =
T[T["length"]] extends MiddlewareHandler<infer _, infer O> ? O : never;

export function compose<T extends readonly MiddlewareHandler[]>(
middlewares: T,
handler: Handler<ExtractOutput<T>>,
): Handler<ExtractInput<T>> { ... }
type ExtractInput<T extends readonly MiddlewareHandler[]> = T[0] extends
MiddlewareHandler<infer I> ? I : never;
type ExtractOutput<T extends readonly MiddlewareHandler[]> =
T[T["length"]] extends MiddlewareHandler<infer _, infer O> ? O : never;

export function compose<T extends readonly MiddlewareHandler[]>(
middlewares: T,
handler: Handler<ExtractOutput<T>>,
): Handler<ExtractInput<T>> { ... }
this however does not work, as it complains when using it like following:
export const auth = {
"/logout": compose(
[session(), authenticated()] as const,
logout
),
"/github": github
} satisfies Routes;
export const auth = {
"/logout": compose(
[session(), authenticated()] as const,
logout
),
"/github": github
} satisfies Routes;
Argument of type 'readonly [MiddlewareHandler<unknown, SessionState>, MiddlewareHandler<SessionState, Required<SessionState>>]' is not assignable to parameter of type 'readonly MiddlewareHandler<unknown, unknown>[]'.
Type 'MiddlewareHandler<unknown, SessionState> | MiddlewareHandler<SessionState, Required<SessionState>>' is not assignable to type 'MiddlewareHandler<unknown, unknown>'.
Type 'MiddlewareHandler<SessionState, Required<SessionState>>' is not assignable to type 'MiddlewareHandler<unknown, unknown>'.
Types of parameters 'ctx' and 'ctx' are incompatible.
Type 'MiddlewareContext<unknown, unknown>' is not assignable to type 'MiddlewareContext<SessionState, Required<SessionState>>'.
Property 'session' is missing in type 'MiddlewareContext<unknown, unknown>' but required in type 'Required<SessionState>'.deno-ts(2345)
Argument of type 'readonly [MiddlewareHandler<unknown, SessionState>, MiddlewareHandler<SessionState, Required<SessionState>>]' is not assignable to parameter of type 'readonly MiddlewareHandler<unknown, unknown>[]'.
Type 'MiddlewareHandler<unknown, SessionState> | MiddlewareHandler<SessionState, Required<SessionState>>' is not assignable to type 'MiddlewareHandler<unknown, unknown>'.
Type 'MiddlewareHandler<SessionState, Required<SessionState>>' is not assignable to type 'MiddlewareHandler<unknown, unknown>'.
Types of parameters 'ctx' and 'ctx' are incompatible.
Type 'MiddlewareContext<unknown, unknown>' is not assignable to type 'MiddlewareContext<SessionState, Required<SessionState>>'.
Property 'session' is missing in type 'MiddlewareContext<unknown, unknown>' but required in type 'Required<SessionState>'.deno-ts(2345)
Plus points: I would ideally also like the T generic type to require that all following MiddlewareHandlers have the its generic I set to the previous O like following type:
[MiddlewareHandler<unknown, A>, MiddlewareHandler<A, B>, MiddlewareHandler<B, C>];
^>------------------->^ ^>------------------->^
[MiddlewareHandler<unknown, A>, MiddlewareHandler<A, B>, MiddlewareHandler<B, C>];
^>------------------->^ ^>------------------->^

Looking for more? Join the community!