-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Type inference defaults to '{}' in call sites. #5254
Comments
|
Well, not doing type inference from call sites will cause this. Is there a reason for that? Can this be a feature request? |
You can though infer the type from the arguments passed the function constructor. The following should work: new Promise((resolve: (value: { foo: number }) => void) => {
resolve({ foo: 1 });
}).then((value) => {
value.foo;
}); |
Which call sites? TypeScript doesn't know that the argument can only be called once. How would you type this? new Promise((resolve) => {
resolve({ foo: 1 });
resolve({ bar: false });
}).then((value) => {
value.foo;
}); |
@kitsonk, You could specify the types in the call-site arguments, but that's just another workaround like specifying the types directly and we lose the benefit of type inference. |
@kitsonk, You could infer the type as |
Having multiple inference sources for the same type parameter isn't a problem (it happens already, e.g. One reason we haven't considered call expressions (or assignments, for that matter) as "valid" inference sites is that they rarely make sense from a runtime perspective. The @rbuckton probably has some input here. |
To help demonstrate the utility of the Promise example, please consider the similar situation where the types are properly inferred in the rest of the thenable chain:
|
Discussion should either continue here or on #5884. |
I know you said "rarely", but that's not the case with promises. It makes no sense immediately, but at the point that the promise resolves, there's clearly a runtime mechanism at work here which makes the promise hold a This might be worth experimenting with. |
The annoying thing is that when typescript fails to infer a type and use {}, there are no errors whatsoever and it compiles just fine. So later during runtime you face issue. |
I worked on a flag like that a while ago, but I think there was the idea that we wouldn't need it going forward. I'm not convinced that's really the case. |
Honestly, as for me the fact that you can have things silently screwed up for you contradicts the whole idea of type safety. |
Can you elaborate on this? When did you get |
It was plenty of times, I will prepare an example and post here. Just need to find some time. |
I don't think I have every encountered a runtime error where I think it is just more unintuitive and surprising, to such a point when I first saw this issue it was logical, why it was the way it was, only to have 7 months later felt totally annoyed that it was the way it was. |
@DanielRosenwasser @yazla @kitsonk: Here is an example of a type error that occurs using Promises due to https://gist.github.com/samlanning/fd9da5d5811dc8f0156ba327df97c5aa Personally, i'm very fond of the solution @DanielRosenwasser worked on in the branch |
pinging @chrisgavinme (colleague working on this together) |
I've narrowed it down to this being the cause of our type errors: let functionWithObjectParameter: (value: {}) => void;
let functionWithNumberParameter: (value: number) => void;
functionWithObjectParameter = (value: string) => {
console.log('i really expect this to be a string:', value);
console.log(value.substr(0));
};
// this assignment above should be considered invalid
functionWithNumberParameter = functionWithObjectParameter; // results in a function we assume should take a number, but actually takes a string.
let objectValue: {} = "string";
let numberValue: number = objectValue; // <- correctly does not type check |
Okay i've thought about it a little more, and here's a (hopefully easily digestible) explanation of why I now feel that Suppose that you have an interface like so: interface Foo<T> {
get(): T;
set(value: T): void;
} When type inference fails, you may end up with a value that has this type: let bar: Foo<{}> = //... If you want to fetch the value, that's fine, you know nothing about that value, you can't do much with it, but that's fine: let value = bar.get(); // <- the type of value is inferred to be {} This next line will not type check, because we don't know anything about the value, this is perfect, this is what is designed to happen when type inference fails: value.doSomething(); // <- this won't type check, perfect. However the following does type check: bar.set(1234); this is bad, because we don't know what the type parameter of TL;DR Inferring the types of function parameters to be |
An ultra simple case where this can easily bite you at runtime, via Twitter: function foo(): Promise<boolean> {
return new Promise(res => res(123));
} This compiles happily with es6-promise.d.ts (inferring Changing to the below to move the generic to the call site catches this correctly: function foo() {
// Fails: number is not assignable to boolean|Thenable<boolean>
return new Promise<boolean>(res => res(123));
} |
Any news at this issue? // @flow
function foo(): Promise<boolean> {
return new Promise(res => res(123));
}
even without generic type specification: // @flow
function foo(): Promise<*> {
return new Promise(res => res(123));
}
foo().then((x) => x.length);
Have you any plans to start do type inference in call sites? |
@ikokostya I believe this is fixed by #16072. In TypeScript
Results in the error: |
…e type Promise<string> itself and needs a little help. See the following for more discussion: microsoft/TypeScript#5884 microsoft/TypeScript#5254
I had this issue bite me when infering a type from an object field that could not exist (optional field); I was expecting to get an infered type of If you're using this in a conditonal type you can catch that default type CoalesceInfer<T, D> = keyof T extends never ? D : T; This works because So in my case: type Something<T> = T extends { params?: infer P, query?: infer Q }
? {
params: keyof P extends never ? undefined : ProcessedType<P>
query: keyof Q extends never ? undefined : ProcessedType<Q>
}
: never or simplified: type SafeProcessedType<T> = keyof T extends never ? undefined : ProcessedType<T>;
type Something<T> = T extends { params?: infer P, query?: infer Q }
? {
params: SafeProcessedType<P>,
query: SafeProcessedType<Q>
}
: never |
Yeah, seems fine by me |
I'm using TypeScript 1.6.3 in Visual Studio 2015. The es6-promise declaration I'm using contains:
The following works with an explicit typing of the Promise generic:
However, shouldn't type inference allow the following to work?
Instead, I am getting an error when accessing value.foo that foo does not exist on type '{}'. Why is TypeScript defaulting to the
{}
type?The text was updated successfully, but these errors were encountered: