The TypeScript docs have a section called "Type inference in conditional types" that gives a nice answer with the Unpacked<T>
type definition:
type Unpacked<T> =
T extends (infer U)[] ? U :
T extends (...args: any[]) => infer U ? U :
T extends Promise<infer U> ? U :
T;
Along with some examples of the results:
type T0 = Unpacked<string>; // string
type T1 = Unpacked<string[]>; // string
type T3 = Unpacked<Promise<string>>; // string
I wasn't familiar with inferred types, so thanks for helping me learn something new! :)
Update addressing additional question:
Is there a way to do this where you force the type at compile time to be of that instance.
Example: type T0 = UnpackedArray<string> // fail because it's not an Array
Yes! It's possible to place constraints on the generic type using extends
:
type UnpackedArray<T extends Array<any>> =
T extends (infer U)[] ? U : never;
type T2 = UnpackedArray<string[]>; // string
type T3 = UnpackedArray<string>; // Error: Type 'string' does not satisfy the constraint 'any[]'.