4

Refering to Unpacked in https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html.

type Unpacked<T> =
    T extends (infer U)[] ? U :
    T extends (...args: any[]) => infer U ? U :
    T extends Promise<infer U> ? U :
    T;

Can there be a recursive version of this? I've tried and I am getting circularly references itself error.

type Unpacked<T> =
    T extends (infer U)[] ? Unpacked<U> :
    T extends (...args: any[]) => infer U ? Unpacked<U> :
    T extends Promise<infer U> ? Unpacked<U> :
    T;

The above code fails due to circularly references.

Recursive version will do this:

Unpacked<Array<Promise<number[]>>> === number;

It's useful when we don't know how deep it needs to unpack.

Joon
  • 9,346
  • 8
  • 48
  • 75

1 Answers1

6

Unfortunately for your definition, conditional types are not allowed to refer to themselves recursively, except in specific situations. From the description of the above-linked pull request introducing the feature:

Similar to union and intersection types, conditional types are not permitted to reference themselves recursively (however, indirect references through interface types or object literal types are allowed, as illustrated by the DeepReadonly<T> example above). For example the following is an error:

type ElementType<T> = T extends any[] ? ElementType<T[number]> : T; // Error

There have been times when people have managed to fool the compiler to allow recursive types like this, but those are also the same situations where someone can write something that crashes the compiler. It's officially a bad idea so I'm not going to try to tell you how to do it that way.

I'd love to see a solution to this also, but it's not there now (as of TS3.0 anyway).


So, workarounds. The most straightforward workaround is to figure out a practical maximum depth you're likely to need, and then just code for that. Here's one way to do it:

type _U<T> =
  T extends (infer U)[] ? U :
  T extends (...args: any[]) => infer U ? U :
  T extends Promise<infer U> ? U :
  T;

// up to 8 levels deep
type Unpacked<T> = _U<_U<_U<_U<_U<_U<_U<_U<T>>>>>>>>

You can verify that this works for your example:

type JustNumber = Unpacked<Array<Promise<number[]>>>; // number

It's not perfect, but it's probably the best you'll get for now. Hope that helps. Good luck!

jcalz
  • 264,269
  • 27
  • 359
  • 360
  • Thanks. That's what I did for now. Hopefully recursion is supported in the future with reasonable stack limit. – Joon Jul 24 '18 at 08:45