-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Type inference ignores explicit type annotation #55330
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
This is working as intended. It's partially a duplicate of #9998 (modification in closure is not considered), and the intentional behavior that assignment narrows the variable (that's why You can work around this by using a type assertion: |
IMHO, this is suboptimal and not the behaviour anyone expects from the explicit type annotation. |
The behaviour is what most people expect. When they assign a The only reason this is "unexpected" is because of #9998. |
This logic should only apply to I find it frustratingly unintuitive. |
Consider this example: let text: string | null = someFunctionReturningStringNotNull();
text.substring(1);
// Later in code:
text = null; Should this code error? You're proposing yes, most people (including me) expect no.
It doesn't. It dictates the type until the next assignment. The assignment in OPs example is just not seen due to #9998. Regardless, this has come up many times already, and the position of the team is clear on this. |
With explicit type annotation? Yes.
Closing as duplicate? |
Without a type annotation the variable is typed |
But the code is explicitly stating possible types: string or null. It's nice to have Typescript infer the initial type on the first assignment, but ignoring explicit annotation in cases where there are no more visible to Typescript assignments in the following code is beyond my understanding. In my opinion, inference should never overrule explicit type annotations. Inference should help, not roadblock. |
Given my example, your suggestion would result in an error on See also this comment: #51487 (comment)
|
My point is: if a variable has an explicit type annotation, then this annotation should have higher priority then the inferred type of the first assignment. In your example, |
And I argue: It should be typed The culprit in the end is still the missed assignments due to #9998. In all other cases this behaviour is what most people would expect (but maybe not you :-p). But alas, we're moving in circles. Another comment by the team: #50444 (comment)
|
Because the code explicitly states
I'm not saying "don't perform narrowing on initialisation". Do it, but respect the explicit type annotation β it's there for a reason. |
Yep, it is. To limit what values can be assigned to it. That's the purpose of the type annotation, and that's still happening. :-D It's working as intended, and the suggested change would be a massive breaking change and be so unnecessarily annoying in a lot of code. Anyway, just circling and rehashing arguments, so I'm out. |
You're proposing that I should have to write: let x: string | null = "foo";
if (x !== null) doStringThing(x);
x = null; Just because I might later assign it a null value. That's nonsense. |
Write this: const x = "foo";
doStringThing(x);
const maybeX = condition ? x : null; |
And the variable has a different scope. So you possible need to declare the variable much earlier, before it has a meaningful value assigned, so it needs to be All that mess and extra code for the small corner case of #9998, which can easily be worked around with a type assertion as demonstrated. |
We don't have to guess which behavior is better. The annotated type used to take precedence over the initializer type, and everyone rightly complained that this example didn't work. It also breaks syntactic consistency with having the initialization on the subsequent line. Complaints about the inconsistency you're describing are much rarer. |
π Search Terms
type inference
explicit type
narrowing
type annotation
π Version & Regression Information
TS version 5.1.6
β― Playground Link
https://www.typescriptlang.org/play?#code/IYZwngdgxgBAZgV2gFwJYHsI1JKBBEEVAcwgFsBTCZACggC4YIEyAjCgJwEpGAFD9GVQgKAHmZtOAPhgBvAFAwlMADYVkMZBQAeyRiGQdUEYjAA+TBCpUwAvJesBueYuUQKAdxj9BwijRoOChB0FQA3Ci47GQVlOM0dDXsAchDKBN1k53jlIJDw-yQAEwo4YwoirlclAF8q6uwPYFQNdy8fIREAvNCIqNsZEWQAFVRKdARaHoKAGhgARgAGZcWuLmcG1DgYGi1dO1t7Zmso2JyYIOQEDixFhpqXOMvrrD3kADo1E2QAC2caoA
π» Code
π Actual behavior
Typescript narrows
text
type tonull
and not tostring | null
π Expected behavior
Expecting to have
string | null
type fortext
before explicit type narrowing (if (text === null)
) andstring
type after explicit type narrowingThe text was updated successfully, but these errors were encountered: