-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Generic type parameter inferred as {} #8922
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
It sounds like you want two main things: (1) variables to have types with unbound type variables. The shortest example of your problem is interface Either<E,V> { e: E, v: V }
declare function pure<E,V>(v: V): Either<E, V>
const x: Either<E,number> = pure(1); // what is E? TypeScript needs to know. TypeScript just can't do this -- it never carries around type variables with a value's variable. And it only infers from argument types. It also looks like you want whole-program type inference, in order to infer the type from two different branches of a conditional: const x: Either<string, number> = true? pure(42) : fail("string"); TypeScript definitely does not do this -- it always works bottom up except when contextually typing, which only applies to a few constructs that would otherwise nearly always need type annotations, like object literals and lambdas. @DanielRosenwasser worked on an error for failure when type inference produces The short summary is that TypeScript's type system is so far from Haskell that it's not feasible to provide many of Haskell's features. |
With the nightly build and strict null checking mode you can get pretty close by using // Compile with --strictNullChecks
interface Either<E, V> { e: E | undefined; v: V | undefined }
function pure<V>(v: V): Either<undefined, V> { return { e: undefined, v }; }
function fail<E>(e: E): Either<E, undefined> { return { e, v: undefined }; }
function f(x: Either<string, number>) {}
const x = pure(1); // Either<undefined, number>
f(x); // Ok
const xx = !!true ? pure(42) : fail("string"); // Either<undefined, number> | Either<string, undefined>
f(xx); // Ok |
TypeScript is allowed to infer it as |
Is the following code caused by the same underlying issue or is this something that is reasonably easy to add? function f<T>(): T { return null;}
function g<T>(x: T): void {}
g<string>(f()); // argument of type '{}' not assignable to parameter of type 'string' |
I have an even simpler example: function f<T>(): T { return null; }
const x: string = f(); // same error |
Yes, this is exactly how type parameter inference works in typescript. It only proceeds from function arguments, so a function with no arguments will just default to {}. Unfortunately, {} and any are both concrete types, not fresh types that will later widen to or unify with some other type from context. Once you have {}, you're stuck with it. And any is even worse. It often infects other expressions. I believe this architecture was chosen for locality of errors in an incremental context (intellisense, basically) and similarity to existing systems that work the same way (C# and Java, basically). @ahejlsberg can comment more about architectural decisions if you're curious. |
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 asEither<{},number>
. IOW, the type parameterE
is made rigid when it should remain generic.Code
A similar issue comes up when inferring the type of this constant:
TypeScript infers
Either<{},number> | Either<string,{}>
instead of the expectedEither<string,number>
.To fix the first problem I could define
pure
to returnEither<any,V>
. But I don't want to do that, to allow TypeScript to automatically inferE
from context. Such as in this case:TypeScript infers
x
asEither<{},{}>
, but it should beEither<string,V>
. TheE
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 functionf
) binds it to a string, it should remain a string.If I change
pure
to returnEither<any,V>
andfail
to returnEither<E,any
> then the type ofx
changes toEither<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.
The text was updated successfully, but these errors were encountered: