Description
TypeScript Version: 1.8.10, also tested with 1.9.0-dev.20160601-1.0 which behaves the same.
The following code should compile but produces an error. It is because the type of x
is inferred as Either<{},number>
. IOW, the type parameter E
is made rigid when it should remain generic.
Code
interface Either<E,V> { e: E; v: V }
function pure<E,V>(v: V): Either<E,V> { return { e: undefined, v }; }
function fail<E,V>(e: E): I<E,V> { return { e, v: undefined }; }
function f(x: Either<string,number>) {}
const x = pure(1);
f(x); // <- error
A similar issue comes up when inferring the type of this constant:
const x = true ? pure(42) : fail("string");
TypeScript infers Either<{},number> | Either<string,{}>
instead of the expected Either<string,number>
.
To fix the first problem I could define pure
to return Either<any,V>
. But I don't want to do that, to allow TypeScript to automatically infer E
from context. Such as in this case:
// The Monad `bind` function
function bind<E,A,B>(x: Either<E,A>, f: (a: A) => Either<E,B>): Either<E,B> {
// Incomplete implementation of this function! Only for illustration purposes.
throw new Error('Implement me!');
}
const x = bind(pure(42), n => fail("error"));
TypeScript infers x
as Either<{},{}>
, but it should be Either<string,V>
. The E
type parameter must be the same in both arguments as well as the return type of the whole function, and because the second argument (the function f
) binds it to a string, it should remain a string.
If I change pure
to return Either<any,V>
and fail
to return Either<E,any
> then the type of x
changes to Either<any,any>
, which is not much better.
It would be nice to have an option like noImplicitAny
to make it an error if TypeScript infers {}
as a type parameter.
I could solve all these issues by always explicitly attaching the types to function calls and var/let/const statements. But that adds a lot of noise to the code, which is particularly annoying for simple cases where TypeScript should be able to infer the correct types.