microsoft/TypeScript

Type Inference does not work for generics with default types

jpkraemer opened this issue · 5 comments

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 '{}'.

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.

closing in favor of #10571

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)
})

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);
}

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