Async Functions
Async functions in TypeScript are simple once you understand what a Promise is under the hood.
The mental model
Every async function, by definition, returns a Promise. Even if you return a literal value like return 42, JavaScript wraps it in a Promise automatically. TypeScript enforces this reality. The return type of an async function must always be Promise<T>, where T is the type of the value you are resolving.
async function fetchUser() {
return { id: 1, name: "Alice" };
}
// => inferred as: Promise<{ id: number, name: string }>
Explicitly typing the Promise
While TypeScript can often infer the return type, it is common in production to explicitly type async function returns, especially when dealing with external APIs where the structure isn't locally known.
type User = { id: number; name: string };
async function getUser(id: number): Promise<User> {
const response = await fetch(`/api/users/${id}`);
const data = await response.json(); // data is 'any' by default
return data;
}
Handling async errors
When typing async functions, it is important to remember that Promise<T> only describes the success path. In TypeScript, thrown errors are unknown by default (or any in older configurations). You do not include the error type in the return signature.
async function safeFetch(): Promise<User | null> {
try {
const res = await fetch("/user");
return await res.json();
} catch (error) {
// error is 'unknown'
return null;
}
}
An async function always returns a
Promise<T>. You use this generic wrapper to describe the shape of the data that will eventually be resolved.