I have made a few decorators to make routing easy with express
(e.i. @Get()
, @Post
) which are used to map class functions to a route.
I would like the targeted method's return type to systematically be of type HttpResponse
and have ts or the IDE throw an error when another type is returned:
export type HttpResponse = { data?: any; status?: number; } | object;
For instance:
@Post()
login(email: string, password: string) : object { // <-- highlights method as returning the wrong type
return {};
}
@Post()
async register(email: string, password: string) : Promise<HttpResponse> { // <-- all good
return {};
}
Can this be achieved? Could this be done using a custom tsconfig
rule?
PS: Decorators are very knew to me and I might be extrapolating how decorators can be used.
I will assume your Post
decorator looks like this
declare function Post(): MethodDecorator
If you look what MethodDecorator
type in TS definitions looks like, you'll find
declare type MethodDecorator = <T>(
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>,
) => TypedPropertyDescriptor<T> | void
It's not clear to me, why this type is not generic (the function it declares it generic, but the type itself is not), TS developpers must've had their reasons for this. Anyway, since you worked with decorators, you know, that this third argument is the descriptor of the property you attach your decorator to, here it's of type WhateverTypeIsUsedToTypeDescriptors<T>
. You can declare your own decorator type that only expects this property to be a function that returns HttpResponse
by replacing this T
with your type:
type HttpMethod = (...args: any[]) => HttpResponse
type HttpMethodDecorator = (
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<HttpMethod>,
) => TypedPropertyDescriptor<HttpMethod> | void
declare function Post(): HttpMethodDecorator
See how this works in TS playground: if I try to decorate a method that returns string
instead of HttpResponse
, it shows an error (for some reason it says that HttpMethod
is not assignable to (email: string, password: string) => string
although you probably would expect it to be vice versa, but whatever).
That's the main idea, now to clean up a little bit you may want to make this type generic:
type MethodDecorator<T extends Function> = (
target: Object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>,
) => TypedPropertyDescriptor<T> | void
declare function Post(): MethodDecorator<(...args: any[]) => HttpResponse>
Also don't forget to let it accept promises
declare function Post(): MethodDecorator<(...args: any[]) => HttpResponse | Promise<HttpResponse>>
Heck, you could even type the function's arguments or basically anything, enjoy :P
Also I'm not sure why you want TS to highlight login
as returning the wrong type, because it returns object
and in HttpResponse
you have whatever | object
so it shouldn't really be an error