Проблема

У TypeScript есть типы, которые оборачивают другие типы, например Promise<T>. Как мы достанем внутренний тип из такого типа? Например, если у нас есть Promise<ExampleType>, то как мы достанем из него ExampleType?

Решение

Интересная проблема, для решения которой нужно знать об одной из возможностей системы типов у TypeScript, которую редко используют, и зря!

Но прежде чем перейдём к объяснению, давайте проанализируем проблему. Автор просит распаковать тип. Что значит “распаковать тип”? Распаковка типа это процесс извлечения внутреннего типа из окружающего его.

Давайте с примером. Если у вас есть тип Promise<string>, то распаковка такого типа Promise приведёт к типу string, так как он внутри Promise.

А теперь о проблеме. Начнём со случая попроще. Если в наш тип Awaited приходит Promise<string>, то нам нужно вернуть string. Иначе, нам нужно вернуть T, потому что это вовсе не Promise, а значит возвращаем без изменений. Для этого воспользуемся условными типами:

type Awaited<T> = T extends Promise<string> ? string : T;

Но в этом решении проблема. Следуя таким путём, мы понимаем только строки в Promise, а нам нужно поддерживать любой тип. То как же всё-таки это сделать? Как достать внутренний тип из Promise, если мы не знаем что там?

Для этих целей, в TypeScript присутствует выведение в условных типах. Вы можете сказать компилятору “как только ты узнаешь что здесь за тип, присвой его моему тип параметру, пожалуйста”. Подробнее об этом можно почитать здесь.

Зная о выведении типов, мы обновим наше решение. Вместо того, чтобы проверять на Promise<string> в нашем условном типе, мы заменяем string на infer R. Это делается потому что мы не знаем что там за тип (пока что). Единственное, что мы знаем, это то что это Promise<T> с каким-то типом внутри.

Как только TypeScript выяснит, что за тип находится внутри Promise, он присвоит его нашему тип параметру R. R станет доступным в нашей правдивой ветке условного типа, откуда мы его и возвращаем.

type Awaited<T> = T extends Promise<infer R> ? R : T;

Что почитать