The main issue at hand is the fact that the generic type parameter in this scenario is not being utilized:
type Foo<T = number> = 123;
type Oops = Foo extends Foo<infer U> ? U : never;
// type Oops = unknown
In this case, Foo<T>
will always be 123
regardless of the value of T
. TypeScript's type system relies on a structural rather than nominal approach. Therefore, without a structural dependency on T
, inferring T
from Foo<T>
will not be reliable, even if it occasionally works. This is why the TypeScript FAQ discourages the use of generic types that do not utilize their type parameters.
Another significant issue is that TypeScript does not consider default type arguments for inference purposes, as discussed in microsoft/TypeScript#42064.
Even if TypeScript treated types nominally, the desired behavior may not be achieved. Contrast this with using constraints, which play a partial role in inference:
type Bar<T extends number = number> = 123;
type Hmm = Bar extends Bar<infer U> ? U : never;
// type Hmm = number
Applying the same logic to Foo<T>
would suggest that Bar<T>
would also encounter issues, but here you obtain number
instead of
unknown</code because the compiler resorts to the constraint when inference fails. Despite <code>Bar<T>
always equating to
123
for all
T
, there is still a constraint of
number
associated with
T
.
Combining both factors indicates that the intended action is unattainable. To rectify this, the approach should involve utilizing the type parameter explicitly and explicitly stating the type without a type argument to prompt the compiler to substitute in the default value, thereby increasing the likelihood of accurately inferring the type.
Playground link to code