question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Inference failing for conditional types in function parameters

See original GitHub issue

When 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:open
  • Created 4 years ago
  • Comments:8 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
weswighamcommented, Feb 6, 2020

OK, so the issue is due to contextual typing; specifically recent additions around generic signatures. The contextual type of query in the functioning case is T - T is unconstrained and has no signatures, which means we forward along the type of query unmodified into inference (which is then immediately matched against T). In the “broken” case, the argument type is FooQueryLike<T>, which does have signatures, which causes us to go “ah, we may need to unify the type parameters in query with the arguments of FooQueryLike. Let’s defer inference to them until the next inference stage”. We then produce unknown as the inference result for T 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, that unknown (from having made no inferences whatsoever), when fed through the arguments, becomes never 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 to extend anything in a conditional, which means those conditions are always going to evaluate to their false branches, which, more often than not, will produce a never which in turn disallows any actual assignments.

0reactions
weswighamcommented, Feb 6, 2020

While a fix to #26933 won’t also fix this, it is a prerequisite, so this is blocked on #26933 (or at least #34882).

Read more comments on GitHub >

github_iconTop 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 >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found