In TypeScript, asynchronous functions (async/await) always return a Promise. The return type is specified as Promise<Type>. When describing a function, it's important to explicitly type the result so that TypeScript correctly works with types inside the promise. Typing errors can occur if the intermediate values do not match those declared.
Asynchronous function:
async function fetchUser(id: number): Promise<User> { const response = await fetch(`/api/user/${id}`); const data: User = await response.json(); return data; }
If the function is supposed to return an error or rejection, the promise type should not directly describe the error (Promise<Error>), as the promise rejection is not typed, but caught through catch.
Typing promise chains is important:
function getNumber(): Promise<number> { return Promise.resolve(42); } getNumber().then(val => val.toFixed(2)); // TypeScript knows that val is a number
Question: Can an asynchronous function return something other than a promise?
Answer: No. An asynchronous function always returns a Promise object. If a non-promise is explicitly returned, TypeScript will automatically wrap the returned value in a promise.
async function test() { return 1; } const result = test(); type ResultType = typeof result; // Promise<number>
Story
A developer wrote a function without explicitly typing the return value, assuming it returned an object of a certain type. When the logic changed, the function started returning a different type, and this was not noticed immediately, as TypeScript, based on type inference, did not issue an error. As a result, errors manifested only at runtime when the function consumer tried to access a non-existent property.
Story
In one project, promise chains were not typed. Attempting to access a non-existent method on the result led to errors. Thus, the result then(response => response.data) was considered any, and the error catching of fields or methods manifested only at runtime. The error went into production and was discovered only with the help of users.
Story
An asynchronous function was declared with the type Promise<Error>, thinking that this would allow catching errors, but in reality, errors were not thrown through throw, and the promise was rejected with a different type of value. This led to implicit mixing of errors and valid values, resulting in bugs in the logic of handling promise results.