Back to the Basics

[typescript] Awaited type에 대해 알아보자 본문

Programming Languages/Typescript & NestJS

[typescript] Awaited type에 대해 알아보자

9Jaeng 2026. 1. 17. 22:03
728x90

최근에 사용하게 된 Awaited type에 대해 알아보고자 한다.

공식문서 링크 : https://www.typescriptlang.org/docs/handbook/utility-types.html#awaitedtype 

 

Documentation - Utility Types

Types which are globally included in TypeScript

www.typescriptlang.org

 

Awaited type이란?

Awaited type은 await 했을 때 실제로 얻는 값의 타입을 의미한다.  이 타입은 typescript 4.5 버전에 새로 추가된 타입이다.

그럼 4.5 버전 이전에는 await 했을 때 실제 얻는 값의 타입을 확인할 수 없었을까? 한다면 그것은 아니다. 아래의 예시와 같이 4.5 이후나 이전에도 중첩된 Promise까지 실제 resolve 되는 결괏값 추론은 정상적으로 되었다

type E = Promise<number>;
type F = Promise<Promise<number>>;
type G = Promise<Promise<Promise<number>>>;
type H = Promise<PromiseLike<Promise<number>>>;

declare const e: E;
declare const f: F;
declare const g: G;
declare const h: H;

async function testFunc(){
	const r1 = await e; // type 추론 number
	const r2 = await f; // type 추론 number
	const r3 = await g; // type 추론 number
	const r4 = await h; // type 추론 number
}

그럼 이 타입이 추가된 이유는 무엇일까?

이 타입이 추가되었던 4.5 버전의 릴리즈 노트를 확인해 보면 이 타입이 도입된 배경을 알 수 있는데, Promise를 재귀적으로 완전히 해제(unwrapping)했을 때 실제로 남는 타입을 제대로 추론할 수 있도록 도입된 타입이다. 

릴리즈 노트의 예시를 4.4 버전과 4.5 버전에서 각각 확인을 해보면 4.4 버전은 아래와 같은 type errror를 확인할 수 있고 4.5 버전은 result의 타입 추론을 확인할 수 있다.

릴리즈 노트 예시

declare function MaybePromise<T>(
  value: T
): T | Promise<T> | PromiseLike<T>;

async function test(): Promise<[number, number]> {
  const result = await Promise.all([
    MaybePromise(1),
    MaybePromise(2),
  ]);

  return result;
}

4.4 버전

Type '[number | Promise<1>, number | Promise<2>]' is not assignable to type '[number, number]'.
  Type 'number | Promise<1>' is not assignable to type 'number'.
    Type 'Promise<1>' is not assignable to type 'number'.(2322)
const result: [number | Promise<1>, number | Promise<2>]

4.5 버전

const result: [number, number]

 

위와 같은 에러가 발생한 이유는 typescript 4.5 이전에는 Promise.all 타입 정의 자체가 튜플 내부 요소의 Promise 해제를 재귀적으로  표현하지 못했기 때문이다. MaybePromise <T>는 런타임에서는 결국 T로 resolve 되지만, 당시의 TypeScript 타입 시스템은 Promise.all이 반환하는 튜플 구조의 객 요소까지 Promise를 해제하지 못했다. 그 결과 [number, number]가 아닌 [number | Promise<number>, number | Promise<number>] 로 추론되어 타입 에러가 발생하였다. 

typeScript 4.5에서는 Awaited<T> 타입이 도입되면서 “Promise를 완전히 해제했을 때의 최종 타입”을 재귀적으로 추론할 수 있게 되었고, 이를 기반으로 Promise.all, Promise.race 등의 타입 정의가 같이 개선되었다. 관련 PR

즉, 이 타입은 이전 typescript에서 추론하지 못했던 복잡한 Promise 구조를 재귀적으로 unwrapping 하여 최종적으로 얻는 값을 추론하기 위해 도입하였다.

언제 어떻게 사용하는가?

내가 사용하는 방식은 함수의 return type을 사용해야 할 때 이다. 

예를 들면 아래와 같이 함수가 있다고 했을 때

async function fetchUser() {
  return { id: 1, name: "jaeng" };
}

// 다른 곳에서 이 함수의 결과 타입이 필요
type User = ReturnType<typeof fetchUser>;
// ❌ 추론 타입 Promise<{ id: number; name: string }>

이렇게 하게 되면 추론 타입은 Promise가 된다. 내가 원하던 추론 타입은 { id: number; name: string }이다. 이럴 때 Awaited를 사용하면 원하는 타입 추론이 가능하다

type User = Awaited<ReturnType<typeof fetchUser>>;
// { id: number; name: string }

특히 TypeORM을 사용할 때, 레포지토리의 쿼리 결과 타입을 서비스 레이어에서 재사용하되 엔티티를 그대로 API DTO로 노출하지 않기 위해 레포지토리 반환 타입을 서비스 내부 타입으로 정의할 때에도 Awaited<ReturnType<…>>를 유용하게 사용할 수 있다.

이와 같이 함수의 반환타입을 정의할 때에도 사용할 수 있고 DTO 등을 만들 때에도 사용할 수도 있겠다.

여기서 ReturnType이란, 함수 선언에 명시된 반환 타입을 가져온다. 링크된 공식문서의 예제를 확인해 보자..!

728x90
Comments