Closed mweststrate closed 7 years ago
What if you wanted to do this?
function f(): ReadonlyArray<Animal> {
const res = [];
res.push(new Cat());
res[0].meow();
return res;
}
If we infer res: ReadonlyArray<Animal>
you will get problems because internally it's a mutable array of Cat
s.
In the particular case you're looking at, we actually will infer the type of res
correctly if it's an array that's pushed to, with no type annotations needed at all. See #11432.
function f() {
const res = [];
res.push(1);
return res;
}
Here we infer f: () => number[]
.
In addition to Andy's point, this would be a pretty big change in our type system. We don't really do inference going "backwards" from latter statements. The type system works off of a declared type which is inferred from its declaration. That type is specialized by subsequent checks and assignments.
While there are the concepts of type argument inference and contextual types (which I mention because they have a sort of back-and-forth interaction), those are limited to the statement level.
@andy-ms your examples are unclear to me; as both will result in compile errors (with strict null checks enabled). With out them, the infered type of f
is any[]
, not number[]
. See this [playground examle](http://www.typescriptlang.org/play/index.html#src=class%20Animal%20%7B%0D%0A%20%20%0D%0A%7D%0D%0A%0D%0Aclass%20Cat%20%7B%0D%0A%20%20meow()%20%7B%0D%0A%20%20%20%20%0D%0A%20%20%7D%0D%0A%7D%0D%0A%0D%0Afunction%20f()%3A%20ReadonlyArray%3CAnimal%3E%20%7B%0D%0A%20%20%20%20const%20res%20%3D%20%5B%5D%3B%0D%0A%20%20%20%20res.push(new%20Cat())%3B%0D%0A%20%20%20%20res%5B0%5D.meow()%3B%0D%0A%20%20%20%20return%20res%3B%0D%0A%7D%0D%0A%0D%0Afunction%20f2()%20%7B%0D%0A%20%20%20%20const%20res%20%3D%20%5B%5D%3B%0D%0A%20%20%20%20res.push(1)%3B%0D%0A%20%20%20%20return%20res%3B%0D%0A%7D%0D%0A%0D%0Aconst%20a%20%3D%20f()%0D%0Aconst%20b%20%3D%20f2())
'Argument of type 'Cat' is not assignable to parameter of type 'never'.'
'Argument of type '1' is not assignable to parameter of type 'never'.'
EDIT: The examples (suprisinginly?) do not give compile errors if noImplicitAny
is enabled
@DanielRosenwasser I can imagine this is hard to solve generically, but I figured it would be pretty convenient, especially where a variable type could be inferred directly from the return statements, and normal type inference cannot deliver a "good" candidate.
Not being able to infer a good candidate is probably limited to empty arrays and empty object literals:
This annoyance is especially noticable with arrays; as []
is always inferred to never[]
, which is probably the only type in the system where I am pretty sure it is in 100% of the cases not the type I am looking for :wink:
A similar case is where a lookup map (object) is returned, but initialized as const res = {}
, and items are added later on the fly, and the return type is something like { [key:string]: Type }
.
That does seem like a bug; I would expect that with implicit any
enabled, []
would always be any[]
, and not never[]
even if --strictNullChecks
is on.
With --noImplicitAny
enabled we use control flow analysis to determine the type of arrays with no type annotation. See #11432. This is why you're not seeing errors in your examples with --noImplicitAny
. It sounds to me like this has the behavior you want without having to depend on return type annotations.
@ahejlsberg, @DanielRosenwasser thanks for the explanation. So, the noImplicitAny
options results indeed in sane type inference in most cases (saddly don't have that flag enabled yet in the project I run into it). Never realized that this option actually influences type inferences, always thought it was just a flag that forces all things to be typed. So this option solves the original issue in many cases, so feel free to close this issue.
The inference without noImplicitAny
of []
to never[]
instead of any[]
still puzzles me though. Is this for some good reason correct, or a bug indeed?
Closing the issue as the original question is answered. (The inferences of []
to never[]
is imho a bug but not the original issue). Thanks for the answers!
TypeScript Version: 2.3.2
In many cases I have a variable that holds a value to be eventually returned from the function. The type of this variable always matches the return type of the function. It would be awesome if the type of that variable can be inferred from the fact that it is returned, using
return
, from the function, if the function has an explicitly declared return type.I am aware that inference is done the other way around, however, we always work with explicit return types on functions. We have linting rules enforcing this, as it is a good practice that helps code reviews and encourages api-first thinking.
Code
So currently I have to write
Expected behavior:
Actual behavior:
Compile error:
'Variable 'res' implicitly has type 'any[]' in some locations where its type cannot be determined.'