You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Allow use of infer in the type parameter of a type declaration #52791
TypeScript's own built-in types have other examples where constraints are repeated in both a type parameter and conditional type. I've also seen cases where developers just don't want to repeat constraints, so they define:
typeRecordValueType<T>=TextendsRecord<any, infer V> ? V : never;
And then incorrect usage results in a type of never, rather than an error.
💻 Use Cases
Improved readability
Less repetition when authoring type declarations
Less frustration when consuming lazily-declared types
This is sort of confusing from a lexical scoping perspective. infer currently only introduces an identifier into the truthy branch of a conditional type, but what's being proposed here would be available... to other type parameters? Only in the RHS? Not super clear what the right answer is.
I don't completely follow the counter-example. infer J above is inside a constraint position, but it's a conditional constraint. When using infer in an "unconditional" constraint, today you get the error:
'infer' declarations are only permitted in the 'extends' clause of a conditional type
Regarding scoping, if infer could be used in an "unconditional" constraint, then the entire RHS becomes the truthy branch. Similar to conditional infers, you couldn't refer to the type elsewhere in the constraint (although perhaps you could infer the type from multiple locations).
This feels like a logical progression following the addition of "extends Constraints on infer Type Variables" added in 4.7. When you use a parameterized type it is already a kind of conditional. The difference is if you don't satisfy the condition, you get a compile error rather than an alternate type.
Reacted by Trey Brisbane, Ken Bellows and Scott Sadler
I'm on board with this bigtime. I actually just asked a question about this exact thing on StackOverflow yesterday and got a comment pointing me here. I absolutely think this makes sense as a feature. When I learned about the infer keyword yesterday, my immediate first thought was, "Why is this only available in conditionals, and not generics broadly?" Thus my SO question. It strikes me as the more natural place for them, honestly. Not saying that they aren't also extremely useful within conditional scopes, but I'm surprised that's where they were added first and exclusively.
Ryan Cavanaugh (@RyanCavanaugh) I don't think your example raises a problem at all. That's just basic scoping and variable shadowing stuff, isn't it? It looks like there's essentially a little scope created by the conditional statement, within the parentheses as you've written it:
(T extends {a: infer J } ? J : number)
I don't think I'd expect that J to be available outside this little scoped ternary, even if infer was possible in the generic. And isn't this sort of confusing syntax already possible today, just on the right hand side? I'm thinking about something like this:
type J = string;
type M<T> = [T extends {a: infer J } ? J : number, J];
In this example, the J in the first element of the RHS array refers to the inferred type, and the J in the second element refers to the outer type. So something like this works fine:
let x:M<{a: Set<number>}> = [new Set(), 'foo']
No TS errors. I think this particular point is moot. It's confusing only if you choose conflicting type names, you know? I don't think it's confusing for the parser.
I'm surprised this hasn't seen more feedback from others, this seems like a pretty powerful possibility. I ran into another similar situation in which this would be useful.
Currently, extracting a generic type has to be done like so:
classMyClass<T>{/* whatever */}typeGenericOfMyClass<U>=UextendsMyClass<infer P> ? P : never;
But TypeScript already enforces that T exists, so the never branch is impossible. Instead, we could just say:
typeGenericOfMyClass<UextendsMyClass<infer P>>=P;
Which saves a couple bytes and also, much more importantly in my opinion, significantly cleans up the types generated by the compiler, especially in situations such as the following, in mapped types:
typeFoo<UextendsRecord<string,MyClass<unknown>>={[KinkeyofU]: U[K]extendsMyClass<infer P> ? P : never;};
...where suddenly, the type generated by the compiler becomes a total mess even though we have already enforced (via the Record<string, MyClass<unknown>> bound) that the never branches are impossible.
Activity
RyanCavanaugh commentedon Feb 16, 2023
I doubt this would meet the bar for inclusion but we can listen for feedback.
There'd probably have to be some new syntax for this, since
infercan already appear inside a constraint position with a different meaning:This is sort of confusing from a lexical scoping perspective.
infercurrently only introduces an identifier into the truthy branch of a conditional type, but what's being proposed here would be available... to other type parameters? Only in the RHS? Not super clear what the right answer is.dinofx commentedon Feb 17, 2023
I don't completely follow the counter-example.
infer Jabove is inside a constraint position, but it's a conditional constraint. When using infer in an "unconditional" constraint, today you get the error:Regarding scoping, if
infercould be used in an "unconditional" constraint, then the entire RHS becomes the truthy branch. Similar to conditional infers, you couldn't refer to the type elsewhere in the constraint (although perhaps you could infer the type from multiple locations).This feels like a logical progression following the addition of "extends Constraints on infer Type Variables" added in 4.7. When you use a parameterized type it is already a kind of conditional. The difference is if you don't satisfy the condition, you get a compile error rather than an alternate type.
kenbellows commentedon Feb 23, 2023
I'm on board with this bigtime. I actually just asked a question about this exact thing on StackOverflow yesterday and got a comment pointing me here. I absolutely think this makes sense as a feature. When I learned about the
inferkeyword yesterday, my immediate first thought was, "Why is this only available in conditionals, and not generics broadly?" Thus my SO question. It strikes me as the more natural place for them, honestly. Not saying that they aren't also extremely useful within conditional scopes, but I'm surprised that's where they were added first and exclusively.kenbellows commentedon Feb 23, 2023
Ryan Cavanaugh (@RyanCavanaugh) I don't think your example raises a problem at all. That's just basic scoping and variable shadowing stuff, isn't it? It looks like there's essentially a little scope created by the conditional statement, within the parentheses as you've written it:
I don't think I'd expect that
Jto be available outside this little scoped ternary, even ifinferwas possible in the generic. And isn't this sort of confusing syntax already possible today, just on the right hand side? I'm thinking about something like this:In this example, the
Jin the first element of the RHS array refers to the inferred type, and theJin the second element refers to the outer type. So something like this works fine:No TS errors. I think this particular point is moot. It's confusing only if you choose conflicting type names, you know? I don't think it's confusing for the parser.
dinofx commentedon May 5, 2023
This would be useful elsewhere too:
When this would be preferred (maybe even without
, V?):fatcerberus commentedon Jun 16, 2023
FWIW implementing this might change behavior since it requires TS to infer type variables from a constraint, which, at present, it doesn't do at all:
From what I understand, this is because inferring from constraints often produces bad inferences.
T99 commentedon Aug 1, 2025
I'm surprised this hasn't seen more feedback from others, this seems like a pretty powerful possibility. I ran into another similar situation in which this would be useful.
Currently, extracting a generic type has to be done like so:
But TypeScript already enforces that
Texists, so theneverbranch is impossible. Instead, we could just say:Which saves a couple bytes and also, much more importantly in my opinion, significantly cleans up the types generated by the compiler, especially in situations such as the following, in mapped types:
...where suddenly, the type generated by the compiler becomes a total mess even though we have already enforced (via the
Record<string, MyClass<unknown>>bound) that theneverbranches are impossible.RyanCavanaugh commentedon Aug 1, 2025
Usually there's another option that doesn't require conditionals
zhangyx1998 commentedon Mar 24, 2026
Dropping in another case that might be relevant.
Working version:
But my first intuition when writing the code was:
This feels much cleaner and non-confusing to me.