Description
TypeScript Version: 2.5.0-dev.20170627
When a generic type has multiple constraints (through extends
in generic declaration), it is very hard to predict which one the compiler uses for type inference. I would expect this to be deterministic/predictable.
Two examples (and excuse the verbosity of the code, found it hard to replicate with less):
Listing 1
class Data<T> {
constructor(v: T) {}
}
type Bound<O> = {
[K in keyof O]: string | number | boolean
}
type Binding<O> = {
[K in keyof O]: O[K] | Data<O[K]> | (() => Data<O[K]>)
}
function bind<O extends Bound<O>>(b: Binding<O>, c: ( (a: O) => {} )): null {
return null
}
var MyComponent = bind({
a: () => new Data<number>(123),
b: new Data<boolean>(true)
},
function({a,b}) {
var j : number = a;
var l : boolean = b;
return null;
});
MyComponent
is type inferred as bind<{},{a: number, b: boolean}>...
as I would expect. This snippet compiles fine.
Listing 2
class Data<T> {
constructor(v: T) {}
}
type Bound<O> = {
[K in keyof O]: string | number | boolean
}
type Binding<O> = {
[K in keyof O]: O[K] | Data<O[K]> | (() => Data<O[K]>) | (() => O[K])
}
function bind<O extends Bound<O>>(b: Binding<O>, c: ( (a: O) => {} )): null {
return null
}
var MyComponent = bind({
a: () => new Data<number>(123),
b: new Data<boolean>(true)
},
function({a,b}) {
var j : number = a;
var l : boolean = b;
return null;
});
Binding<..>
now has an additional map value in the form of | (() => O[K])
. For some reason, this triggers MyComponent
to be inferred as bind<{}, { a: string | number | boolean, b: string | number | boolean }>
(which is the type of the Bound
). And it then fails to compile because the assignment in the function is incorrect.
I can certainly see type inference under these kind of complex conditions is never going to be trivial, but it's really hard to have any clue as to what's happening here.
Does it work like this because of the spec, because (of some exploration bounds in) the implementation? And is there something I can do to force the inference towards the first case?
I'm posting here as to maybe have a more generic discussion about how such complex cases might be made more approachable by normal programmers. If this is better suited on SO, please do close the ticket.