Promise<T> Async Types
Last updated: 2026-10-22
Promise<T> Async Types
Async types revolve around Promise<T>: an async function's return type is automatically wrapped in a Promise, and await unwraps it at the value level; the utility type Awaited<T> does the same at the type level (unwrapping nested Promises all the way down). Once you know "what type lives inside the Promise", every variable in async code gets a precise type.
| Group | Engineering & Practice |
|---|---|
| Kind | Syntax |
| Strict mode | Not required |
ℹ️ Info The editor transpiles TypeScript to JavaScript and runs it (no type checking); see each example's Output for type-check results.
📝 Syntax
// async functions: the return type is always wrapped in Promise<T>
async function fetchUser(id: number): Promise<User> {
const res = await getJson("/users/" + id); // await unwraps one layer
return res; // the returned value is User
}
// Awaited<T>: await at the type level
type A = Awaited<Promise<User>>; // User
type B = Awaited<Promise<Promise<User>>>; // User, nested promises unwrap too
⚙️ Parameters
| Promise<T> | A built-in generic container; T is the type of the value you eventually get. Works with async/await out of the box, no imports needed |
|---|---|
| async | Function modifier: allows await inside; the return value is auto-wrapped in a Promise — plain T becomes Promise<T>, existing Promises are not double-wrapped |
| Awaited<T> | Built-in utility type mimicking await: unwraps Promise<T>, recursively unwraps nesting, and passes non-Promise types through unchanged |
Returns:An async function returns Promise<T>: whether the body returns a plain T or a Promise<T>, the final type is Promise<T> (never Promise<Promise<T>>).
▶ Example
Edit the code below and click Try it to see the transpiled result:
Output:
{"id":7,"title":"Write TypeScript notes","done":false}
todo#7 title="Write TypeScript notes" done=false
(Runtime output after transpilation; todo is inferred as Todo through await — without await you hold a Promise<Todo>, and todo.id directly is a type error)
💡 Notes
ℹ️ Info Inside an async function, return T and return Promise<T> are exactly equivalent — the type never stacks into Promise<Promise<T>>; Awaited<T> is that rule turned into a utility type.
💡 Tip Promise.all yields a tuple: Promise.all([p1, p2]) has type Promise<[A, B]>, preserving each position's type — one await gets all values with precise individual types.
⚠️ Note A throw inside an async function becomes a rejected Promise rather than a synchronous throw: wrap the await in try/catch or chain .catch(), or you get an unhandled rejection; the Promise<T> return type itself does not reveal possible failure.
🏷️ Related Engineering & Practice
💡 Related cases
More cases coming soon.
📚 Related tutorials
❓ FAQ
Q What does Awaited<T> do and when would I use it?
A It mimics await at the type level: Promise<T> becomes T, nested Promises unwrap fully, non-Promises pass through. You rarely use it directly — it mostly appears inside built-in typings (Promise.all, async return types) and in your own utilities that need to unwrap a Promise, e.g. Unwrap<T> = Awaited<T>.
Q What happens if I forget await?
A You hold a Promise<Todo>, not a Todo: accessing .id is a type error (more obvious under strictNullChecks), and at runtime it is a task that has not finished. TypeScript will not await for you — whenever you see a Promise type, ask whether you need await.
Q Do I have to write Promise<T> explicitly for async functions?
A No — inference derives Promise<T> from the return statements. Writing it explicitly pays off for public APIs (the contract is visible and signature errors surface first); let small internal functions infer.