#typescript
#typescript
Вопрос:
Я пытаюсь создать цепочку промежуточного программного обеспечения со статической проверкой типа. Он немного имитирует декораторов.
// Should infer type Handler<InputContext, { a: string, b: boolean }>,
// but gets Handler<InputContext, unknown>
export const myHandler = withMiddleware(
addToContextMW(c => ({ y: 10 })),
addToResponseMW(r => ({ b: true })),
async ({ x, y }): Promise<MyResponse> => {
// y: number infers fine!
return {
a: `x = ${x y}`
};
}
);
Игровая площадка TypeScript с полным примером
Полный код:
// Context is just an object with input data
declare type Handler<C, R> = (
context: C
) => Promise<R>;
// Gets a Handler and returns a new one
declare type Middleware<C, NC, NR, R> = (
next: Handler<NC, NR>
) => Handler<C, R>;
declare interface InputContext {
x: number;
}
// In my case the argument with which I'll be calling handlers
// is always the same (InputContext), so I've fixed the input type
declare function withMiddleware<Res>(
handler: Handler<InputContext, Res>
): Handler<InputContext, Res>;
declare function withMiddleware<Req, Res, Z>(
mw1: Middleware<InputContext, Req, Res, Z>,
handler: Handler<Req, Res>
): Handler<InputContext, Z>;
declare function withMiddleware<B, Req, Res, Y, Z>(
mw1: Middleware<InputContext, B, Y, Z>,
mw2: Middleware<B, Req, Res, Y>,
handler: Handler<Req, Res>
): Handler<InputContext, Z>;
// Basic MW, does nothing, just passing the data through
const testMiddleware = <C, R>(): Middleware<C, C, R, R> =>
(next) => (context) => next(context);
interface MyResponse {
a: string;
}
// Should inter type Handler<InputContext, MyResponse>, but gets Handler<InputContext, unknown>
export const myHandler1 = withMiddleware(
testMiddleware(),
testMiddleware(),
async ({ x }): Promise<MyResponse> => {
// x: number from InputContext infers fine!
return {
a: `x = ${x}`
};
}
);
declare type Merge<A, B> = Omit<A, keyof B> amp; B;
// More advanced MWs, change context and/or response types.
const addToContextMW = <C, FR, R>(factory: (context: C) => FR): Middleware<C, Merge<C, FR>, R, R> =>
(next) => (context) => next({ ...context, ...factory(context) });
const addToResponseMW = <C, FR, R>(factory: (response: R) => FR): Middleware<C, C, R, Merge<R, FR>> =>
(next) => async (context) => {
const res = await next(context);
return { ...res, ...factory(res) };
};
// Should infer type Handler<InputContext, { a: string, b: boolean }>, but gets Handler<InputContext, unknown>
export const myHandler2 = withMiddleware(
addToContextMW(c => ({ y: 10 })),
addToResponseMW(r => ({ b: true })),
async ({ x, y }): Promise<MyResponse> => {
// y: number infers fine!
return {
a: `x = ${x y}`
};
}
);
Проблема в том, что, хотя типы выводятся нормально в одном направлении (от ввода к обработчику (последний аргумент)), они не выводятся в противоположном направлении. Что я делаю не так? Или это слишком много для компилятора?