i am trying to implement a type assertion wrapper for function calling Express'es chain res.status(..).json(..). I think i am "already there", but i stuck. The goal is to statically check whether:
- passed HTTP status code conforms certain response payload
- response payload has certain value
TypeScript playground demo (full implementation attempt): click
One of errors from the list i receive in above linked TS demo code [x]:
Property '200' does not exist on type 'THTTPStatusCodeToData'
Wrapper implementation is:
function responseWrapper<
DataKey extends keyof IEmbracedResponse
>(
res: Res,
status: keyof typeof mappedHTTPStatusCode,
data: Record<
DataKey,
THTTPStatusCodeToData[typeof status] // <-- so far, problem is here [x]
>
) {
return res.status(status).json(data); // just a chain call, but TS asserts correct `data` type for certain `status`
}
Example usage:
router.get('/', (req: Request, res: Response) => {
if (!req.body.login) {
return responseWrapper(res, 400, { error: 'Request payload lacks of "login"!' });
}
return responseWrapper(res, 200, { payload: { isAdmin: true }});
})
Example inputs and TS expected type checking results:
responseWrapper(res, 200, { exception: Error('ups') }); // <-- fail, because 'exception' key cannot be associated with 200 status
responseWrapper(res, 500, { exception: 'something crashed'}); // <-- fail, because 'exception' cannot be a string, but Error object
responseWrapper(res, 200, { something: null}); // <-- fail, because status 200 cannot be associated with 'something' key
responseWrapper(res, 500, { error: 'ups' }); // <-- fail, because status 500 must be associated with 'exception' key, not the 'error' key
responseWrapper(res, 200, { payload: { isAdmin: true }}); // <-- ok, because status 200 can be associated with 'payload' key and the payload has object value
responseWrapper(res, 500, { exception: Error('ups') }); // <-- ok, because status 500 can be associated with 'exception' key and the value is Error object
responseWrapper(res, 400, { error: 'ups' }); // <-- ok, because status 400 can be associated with 'error' key and it's a string
So far i've used less advanced wrapper, just to check if passed object value is correctly associated with the key, but now i also want to have HTTP status regarding value association check:
const embraceResponse = <
Key extends keyof IEmbracedResponse
>(response: Record<Key, IEmbracedResponse[Key]>) =>
response;
// usage examples:
res.status(200).json(embraceResponse({ payload: { prop: true } }));
res.status(400).json(embraceResponse({ error: 'ups' }));