Open ivstiv opened 1 year ago
I was wondering this myself. I think this can be accomplished in Fastify TypeScript. This is a declaration from https://github.com/fastify/fastify/blob/main/types/reply.d.ts
export interface FastifyReply<
RawServer extends RawServerBase = RawServerDefault,
RawRequest extends RawRequestDefaultExpression<RawServer> = RawRequestDefaultExpression<RawServer>,
RawReply extends RawReplyDefaultExpression<RawServer> = RawReplyDefaultExpression<RawServer>,
RouteGeneric extends RouteGenericInterface = RouteGenericInterface,
ContextConfig = ContextConfigDefault,
SchemaCompiler extends FastifySchema = FastifySchema,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
ReplyType extends FastifyReplyType = ResolveFastifyReplyType<TypeProvider, SchemaCompiler, RouteGeneric>
> {
// ...
code(statusCode: number): FastifyReply<RawServer, RawRequest, RawReply, RouteGeneric, ContextConfig, SchemaCompiler, TypeProvider>;
// ....
}
So, this might be possible to discriminate in the return type of .code()
narrowing ReplyType
down, but I'm not sure that TypeScript allows to do this...
In the meantime, I'm going to use this function
function typesafeReply<ResponseDtos extends Record<number, unknown>>(
reply: FastifyReply
) {
return <Code extends number>(code: Code) => {
return (replyData: ResponseDtos[Code]) => {
return reply.code(code).send(replyData);
};
};
}
// ...
const MyResponseDtos = {
200: zod.object({ r: zod.string(), n: zod.number() }),
400: zod.string(),
};
type MyResponseDtos = {
[P in keyof typeof MyResponseDtos]: zod.infer<
(typeof MyResponseDtos)[P]
>;
};
typesafeReply<MyResponseDtos>(reply)(200)('string');
// ^ Error
typesafeReply<MyResponseDtos>(reply)(400)('string');
// ^ Ok
@ivstiv what did you end up doing?
I wrote a similar function to yours but it felt ugly to be passing types, reply and the actual response data, so I decided to keep it simple and reverted back to return rep.code().send(). Yes it is opening the door for a human error in the status codes but equally my response objects have status code literals in them, so it would be a pretty obvious mistake.
I also tried writing a fastify plugin to decorate the reply (+ declaration merging) with a generic function that infers types from the response schema, but to no avail. May be I need to step up my type game to get it right.
Oh, I feel that this is a nasty typescript problem to solve. Either it works and it is a miracle or you spend hours ultimately getting nothing, hehe. Anyway, thanks for sharing
Check this out: https://www.youtube.com/watch?v=9N50YV5NHaE
I am wondering if there is an integrated way to restrict the response type based on the status code being set in the request at compile time? I guess I can write my own function to set the status code and return the response doing the discrimination myself but it does seem like a feature that should be part of the library.