Reputation: 82
I have the following function that allows me to handle async errors similar to Go.
type AsyncResults<ErrorType, DataType> = Promise<
readonly [undefined, DataType] | readonly [ErrorType, undefined]
>;
const asyncHandler = <ErrorType, DataType>(
asyncFunc: Promise<DataType>,
): AsyncResults<ErrorType, DataType> => {
return asyncFunc
.then((data: DataType) => [undefined, data] as const)
.catch((error: ErrorType) =>
Promise.resolve([error, undefined] as const),
);
};
When I call it, I usually get error type to be 'unknown', which is ok, but my result type is always whatever is inferred union undefined
const [err, verifiedUser] = await asyncHandler(
getPasswordVerifiedUser(db, {
email,
password,
}),
);
if (err) throw err
const user: VerifiedUser = {
firstName: verifiedUser.firstName (TS complains that object may be undefined)
}
So verifiedUser above has inferred type 'Document | undefined'. Is there a way for TypeScript to know that if err is undefined then verifiedUser cannot be undefined (and vice versa)? I want that if I handle the err by throwing or returning, TS recognizes that verifiedUser can no longer be undefined.
Upvotes: 2
Views: 212
Reputation: 31815
What you are trying to do is impossible. TypeScript does not make any difference between a resolution and a rejection. This is how reject
and resolve
are being typed:
reject<T = never>(reason?: any): Promise<T>;
resolve<T>(value: T | PromiseLike<T>): Promise<T>;
They both return Promise<T>
so there is no possible way to narrow down a Promise
's return type based on the control-flow.
More information about this here: https://github.com/Microsoft/TypeScript/issues/7588#issuecomment-199079907
Edit:
If you want to narrow the tuple type union, just don't destructure it:
// Considering ErrorType is number and DataType is { firstName: string }
const result = await asyncHandler<number, { firstName: string }>(
getPasswordVerifiedUser<{ firstName: string }>(),
);
if (result[0] !== undefined) throw result[0]
const user = {
firstName: result[1].firstName // works
}
It's the only solution that I found and it seems to be confirmed here: Control flow not type-narrowing with union tuple?
Upvotes: 1