Inference failing for conditional types in function parameters
See original GitHub issueWhen using generics with conditionals in a function’s signature, type inference seems to break down. I narrowed the problem down to the following reproducible case. Interestingly, the issues vanishes when another generic “helper” parameter is introduced, as can be seen in the example.
TypeScript Version: 3.6.3, 3.6.2, 3.5.x
Search Terms: parameter, inference, functions, generic, conditional types
Code
export interface Foo<T> {
bar: T
}
export type FooType<T> = T extends Foo<infer U> ? U : never
export type FooLike<T> = T extends Foo<FooType<T>> ? T : never
export type FooQueryType<T> =
T extends (foo: infer U, ...args: any[]) => boolean
? U extends FooLike<infer V>
? V
: never
: never
export type FooQueryParameters<T> =
T extends (foo: any, ...args: infer U) => boolean
? U
: never
export type FooQueryLike<T> =
T extends (foo: FooQueryType<T>, ...args: any[]) => boolean
? T
: never
export type FooQuery<T> = (foo: FooLike<T>) => boolean
export function matchWithBrokenInference<T>(
fn: FooQueryLike<T>, ...args: FooQueryParameters<T>
): FooQuery<FooQueryType<T>> {
return foo => fn(foo, ...args)
}
export function matchWithCorrectInference<T, U extends FooQueryLike<T>>(
fn: T, ...args: FooQueryParameters<U>
): FooQuery<FooQueryType<U>> {
return foo => (fn as U)(foo, ...args)
}
function query<T extends Foo<string>>(foo: FooLike<T>, data: string) {
return true
}
const q1 = matchWithBrokenInference(query, "test") // incorrect inference of query
const q2 = matchWithCorrectInference(query, "test")
Expected behavior:
matchWithBrokenInference
infers query
correctly.
Actual behavior:
matchWithBrokenInference
infers query
incorrectly.
Playground Link: reproducible example
Related Issues: None found
Issue Analytics
- State:
- Created 4 years ago
- Comments:8 (7 by maintainers)
Top Results From Across the Web
TypeScript Conditional Type Inference in Function Parameters
When Conditional Type Inference Does not Happen. TypeScript fails to infer here that the type parameter Something is string literal type 'easy' ...
Read more >Function argument type inference and conditional type
I have a function which accepts a boolean and returns value of RealItem | ImaginaryItem type. I'm using conditional type to narrow the...
Read more >Documentation - Conditional Types - TypeScript
Create types which act like if statements in the type system.
Read more >Advanced TypeScript: The Power and Limitations of ... - Medium
... type arguments. The same way a function has arguments. ... The infer keyword can go inside of a conditional type to the...
Read more >Understanding infer in TypeScript - LogRocket Blog
The infer keyword and conditional typing in TypeScript allow us to ... by first checking whether your type argument ( T ) is...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
OK, so the issue is due to contextual typing; specifically recent additions around generic signatures. The contextual type of
query
in the functioning case isT
-T
is unconstrained and has no signatures, which means we forward along the type ofquery
unmodified into inference (which is then immediately matched againstT
). In the “broken” case, the argument type isFooQueryLike<T>
, which does have signatures, which causes us to go “ah, we may need to unify the type parameters inquery
with the arguments ofFooQueryLike
. Let’s defer inference to them until the next inference stage”. We then produceunknown
as the inference result forT
during that first stage, since we made no successful inferences. Before moving onto the next stage, we check that none of our inferences have already made the signature invalid, but, whatdoyouknow, thatunknown
(from having made no inferences whatsoever), when fed through the arguments, becomesnever
for the first argument and triggers an error, causing us to bail on the second inference phase and immediately issue an error. Generally speaking we assume that when a type parameter is instantiated with it’s constraint, a call should succeed, as the constraint it supposedly strictly “broader” than the type parameter itself; but conditionals throw a massive wrench into this, as the constraint of an unconstrained type parameter isn’t going toextend
anything in a conditional, which means those conditions are always going to evaluate to theirfalse
branches, which, more often than not, will produce anever
which in turn disallows any actual assignments.While a fix to #26933 won’t also fix this, it is a prerequisite, so this is blocked on #26933 (or at least #34882).