Skip to content

Type Inference does not work for generics with default types #19205

Description

TypeScript Version: 2.6.0-dev.201xxxxx

Code

type FormattedDataProvider<T, R = {}> = {
  src: R,
  format: (item: R) => T
}

let a: FormattedDataProvider<string> = {
  src: { a: 23 },
  format: v => String(v.a)
}

Expected behavior:

This should compile and infer that v is of type {a: number}.

Actual behavior:
v is inferred to the default type {}, which results in an error:
error TS2339: Property 'a' does not exist on type '{}'.

Activity

  1. mhegazy commented on Oct 16, 2017

    @mhegazy
    Contributor

    Currently generic type inference happens only when no generic type arguments are provided. specifying at least one disables all generic inference.
    #10571 tracks allowing partial generic inference for required parameters. I would say we will do the default automatic inference in the same change.

  2. mhegazy commented on Oct 16, 2017

    @mhegazy
    Contributor

    closing in favor of #10571

  3. jpkraemer commented on Oct 17, 2017

    @jpkraemer
    Author

    Thanks for the clarification, indeed the following example works:

    function test<T, R>(param: FormattedDataProvider<T, R>) {
      console.log(param); 
    }
    
    test({
      src: { a: 23 },
      format: v => String(v.a)
    })

    This one however, does not. So even when not specifying any types, the defaults might disable inference, too, in some instances.

    type FormattedDataProvider<T, R = {}> = {
      src: R,
      format: (item: R) => T
    }
    
    type UnformattedDataProvider<T> = {
      src: T
    }
    
    type ConfiguredDataProvider<T> = FormattedDataProvider<T> | UnformattedDataProvider<T>;
    
    function test<T>(param: ConfiguredDataProvider<T>) {
      console.log(param); 
    }
    
    test({
      src: { a: 23 },
      format: v => String(v.a)
    })
  4. mhegazy commented on Oct 17, 2017

    @mhegazy
    Contributor

    in the above example, you have used FormattedDataProvider<T> in the definition of ConfiguredDataProvider<T> without a second argument. this is identical to FormattedDataProvider<T , {}>.

    if you wanted to propagate the optionality of the second argument you would write it as:

    type FormattedDataProvider<T, R = {}> = {
        src: R,
        format: (item: R) => T
    }
    
    type UnformattedDataProvider<T> = {
        src: T
    }
    
    type ConfiguredDataProvider<T, R = {}> = FormattedDataProvider<T, R> | UnformattedDataProvider<T>;
    
    function test<T, R = {}>(param: ConfiguredDataProvider<T, R>) {
        console.log(param);
    }
  5. mhegazy commented on Oct 31, 2017

    @mhegazy
    Contributor

    Automatically closing this issue for housekeeping purposes. The issue labels indicate that it is unactionable at the moment or has already been addressed.

  6. locked and limited conversation to collaborators on Jun 14, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions