-
Notifications
You must be signed in to change notification settings - Fork 12.8k
never-returning branches should not be considered when determining whether a property is initialized in a constructor #56362
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
Make your function a Your lack of annotation for |
@MartinJohns Thanks, I was a bit hasty creating the repro here and have updated it to include However, this is is an issue I've run into for a long time (@Andarist finally convinced me to create it π
) and if you don't return, you lose narrowing. Another option would be to narrow |
It seems to work as expected when @MartinJohns 's suggested changes are made: const throwParseError: (_: unknown) => never = (def: unknown): never => {
throw new Error(`${def} is not a valid definition`)
}
class Foo {
// Property 'bar' has no initializer and is not definitely assigned in the constructor.(2564)
bar: string
constructor(def: string | null) {
if (def === null) {
// this return pattern is useful for narrowing
// if you remove `return` here, the initialization error goes away
// but the assignment to bar is then an error
throwParseError(null)
}
this.bar = def
}
} |
This is not the case when you use a |
@nmain Interesting that if you annotate However, in this case as in other cases (e.g. using an arrow function in an |
@MartinJohns TS should be able to identify this as a never-returning function: const throwParseError = (def: unknown): never => {
throw new Error(`${def} is not a valid definition`)
} |
There is an open issue about this that I just don't have at hand right now. Will edit the comment when I found it. |
Based on @nmain's workaround, it seems like this is likely a duplicate of the issue I logged for resolving the signature of an arrow function at this stage: @MartinJohns this is probably what you mean I will leave this open for now but fine with me if the team confirms it's the same underlying issue and wants to track it there. |
I think I'm thinking of another issue that I can't seem to find on mobile right now, but that issue you linked works as well. :-D |
I think the main problem with these is that unless you know about this nuance of when TypeScript resolves arrow function signatures and how it can use them, you'd never think to explicitly annotate the variable as a workaround (in addition to the syntax being much clunkier). Even having logged that other issue, it didn't occur to me until I saw @nmain's repro that it was related, especially since in this case there is no explicit |
I found this which is highly related to your last comment: #36067 I think it should be added to the FAQ. |
@MartinJohns Thank you, it's great to know that exists! Documentation would be nice, but IMO in this case it should just be fixed. This is a lot less messy than most CFA scenarios in TS. Just check if a variable is an arrow function with an annotated return, and if so treat it like a normal |
We discussed this option and decided that the inconsistency of some objects needing an explicit type annotation and some not felt more like a bug than the existing behavior |
@RyanCavanaugh Can you give an example of these objects? It seems like a top-level arrow function is such a common case, and it's clearly analogous to the annotated form like I also do feel like regularly there are cases where explicit returns are required (e.g. for recursive functions) and while there may be cases where TS ideally could infer them, I don't consider those bugs. I don't consider this exactly a bug either, but I do feel like it is hard to deny handling that case explicitly would result in an overall better experience for most TS devs. |
const obj = {
blah: (): never => { throw null }
}
// Why not controlflowed? Seems like a bug!
obj.blah(); |
With both of those examples in mind we now have 2 situations that look like a bug whereas by recognizing arrows weβd have just one ;p I see your overall point but since arrows are just so common (and some linters even enforce them)β¦ it just feels to me that they could be recognized purely on grounds of pragmatism |
@RyanCavanaugh I disagree that it would seem like a bug for control flow not to work in that case. I think all of these cases basically fall onto a spectrum based on how common they are and how expensive they would be to detect. The top-level arrow function case seems a) extremely common and b) very cheap to detect. It's clear that once you start getting into objects like this, the trade-offs are not going to be as good, so I don't think anyone could fault you for special casing top-level arrow functions only. |
I'm generally a pragmatist too, but we also get a lot (a LOT) of feedback from people who just want the language to be "consistent" even at the expense of convenience. People will demand, for example, that the documentation spells out every single case that does and doesn't work, once you go beyond a simple rule. |
@RyanCavanaugh Anyone who would complain about this is trollingπ§ Just |
If you define a new rule around this then it will behave consistently (according to the defined rule) π |
@Andarist Right, this is how @ahejlsberg originally described the criteria for having an "explicit type" in #32695 (comment):
Even as stated, I think the word This would not be ambiguous with regard to cases like the object @RyanCavanaugh mentioned, which would not match any of these criteria. |
This issue has been marked as "Declined" and has seen no recent activity. It has been automatically closed for house-keeping purposes. |
π Search Terms
constructor, initialization, initialized, 2564, never, throw, error
π Version & Regression Information
β― Playground Link
https://tsplay.dev/w6Q9yW
π» Code
π Actual behavior
TypeScript reports that
Property 'bar' has no initializer and is not definitely assigned in the constructor.(2564)
π Expected behavior
TypeScript should identify that in every viable branch of the constructor,
bar
is assigned.Additional information about the issue
No response
The text was updated successfully, but these errors were encountered: