- To summary the question, I'm asking that is unioning type inside generic and outside generic makes different type.
I'm making API server with express and typescript, and I made wrapper function because return type is formed.
The wrapper function calles this Handler.
export type Handler = (req: Request,
res?: express.Response,
next?: express.NextFunction) => (Response<any> | Promise<boolean>)
If handler returns object, wrapper response with this value. And if handler returns boolean, it does another action like calling next router.
Response is formed like this:
type Response<T> = Promise<ResponseSuccess<T> | ResponseError<T>>;
export type ResponseSuccess<T> = {
data: T
}
export type ResponseError<T> = {
error: string
code: number
data?: T
}
And I made auth handler like this:
export const auth = (permission?: Object): Handler => {
return async (req, res) => {
if (!req.auth) return {error: 'Authorization required', code: 401};
if (typeof permission === 'object') {
for (let key in permission) {
if (req.auth.permission[key] !== permission[key]) return {error: 'Permission denied', code: 403};
}
}
return false;
}
}
I thinked that Response<any> | Promise<boolean>
becomes Promise<ResponseSuccess<T> | ResponseError<T>> | Promise<boolean>
, and it becomes Promise<ResponseSuccess<T> | ResponseError<T> | boolean>
. And auth returns ResponseError or false, so I thinked it makes sense.
But the error occurs when I tries to build it:
error TS2322: Type '(req: Request, res: Response<any, Record<string, any>>) => Promise<false | { error: string; code: number; }>' is not assignable to type 'Handler'.
Type 'Promise<false | { error: string; code: number; }>' is not assignable to type 'Promise<boolean> | Resp<any>'.
Type 'Promise<false | { error: string; code: number; }>' is not assignable to type 'Promise<boolean>'.
Type 'false | { error: string; code: number; }' is not assignable to type 'boolean'.
Type '{ error: string; code: number; }' is not assignable to type 'boolean'.
41 return async (req, res) => {
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42 if (!req.auth) return {error: 'Authorization required', code: 401};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
48 return false;
~~~~~~~~~~~~~~~~~~~~~
49 }
The error disappears when I remove return false;
.
What is the problem? Is unioning inside generic and outside generic makes different result? Or there is another error I didn't found?
CodePudding user response:
It would be a better idea to define Response<T>
to be ResponseSuccess<T> | ResponseError<T>
rather than making it a Promise
. It makes the three Response
types consistent with one another and makes it clearer when something is really a Promise
. Then Handler
's return type can be Promise<Response<any> | boolean>
.
is unioning type inside generic and outside generic makes different type.
Yes. For example, this expression...
const _: Promise<string> | Promise<number> = new Promise(() => Math.random() === 0 ? "A" : 1);
...causes this error.
Type 'Promise<string | number>' is not assignable to type 'Promise<string> | Promise<number>'.
Type 'Promise<string | number>' is not assignable to type 'Promise<string>'.
Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
In this particular case I believe they are functionally equivalent but that’s not true in general. For example [1, “A”]
can be typed as (string | number)[]
but it is neither a string[]
nor a number[]
.
The error disappears when I remove return false;.
return false
is outside of the anonymous async
function, it's in auth
itself.