Skip to content

Inconsistent Type Inference of Object Property In Union Type #48500

Closed
@sullivan-sean

Description

@sullivan-sean

Bug Report

🔎 Search Terms

parameter 'x' implicitly has type 'any'
type inference
discriminated union
discriminant property
type narrowing of parent object

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ

⏯ Playground Link

Playground link with relevant code

💻 Code

type Closure<T> = { arg: T, fn: (_: T) => void };

const a: Closure<`x${string}`> | Closure<number> = {
  arg: 0,
  // x is inferred as number, because of arg above
  fn: (x) => {}
}

const b: Closure<string> | Closure<number> = {
  arg: 0,
  // x cannot be inferred (parameter 'x' implicitly has type 'any')
  fn: (x) => {}
}

🙁 Actual behavior

Inference of the parameter to the function field fn is inconsistent across the two objects. The only difference is the type T in Closure<T> | Closure<number> for the object type:

  • When T is boolean, "a" | "b", null or even `x${string}`, the type of x as number is correctly inferred when arg is set to 0.
  • When T is string, never, number[], inference of x as type number is not possible.

See TS playground link for more examples of which types inference is possible for.


There is no immediately obvious pattern as to what types result in possible inference and what types preclude it.

I'm no expert on how this inference is accomplished, but if it is at all similar to type narrowing then this comment #30506 (comment) is the closest explanation I can find. Maybe type inference works when T is boolean ~ true | false and "a" | "b" because arg is then discriminant property. Even in this case, it would seem a field of type `x${string}` would be a discriminant property(?) and I can't wrap my head around why a field of type string could not be a discriminant property if one of type `x${string}` can be

🙂 Expected behavior

Type inference of the parameter x to property fn is consistent across types T in Closure<T> | Closure<number> when arg is set to 0 and 0 is not an instantiation of T

OR

If type inference is not meant to be consistent due to a design limitation (e.g. as in #33205 (comment)), this behavior difference and any associated design limitation is better/more clearly documented -- I'd love to see something on discriminant properties in official documentation, as I found the Github threads a little hard to follow

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions