You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Quite often while working with ImmutableJS data structures and other immutable libraries people forget that values are immutable:
They can accidentally write:
obj.set('name', 'newName');
Instead of:
let newObj = obj.set('name', 'newName');
These errors are hard to discover and they are very annoying. I think that this problem can't be solved by tslint, so I propose to add some sort of "you must use the result" check into the compiler. But right now I have no idea how I want it to be expressed in the language, so I create this issue mainly to start a discussion.
Rust lang, for example, solves a similar problem with #[must_use] annotation. This is not exact what we want, but it's a good example and shows that the problem is quite common.
but linter does not have type information I suppose. so it will be very noisy about legal cases
tslint has access to all the information that the TypeScript compiler has when compiling code, because it integrates to the language services of TypeScript. For example there are several custom rules that teams have Microsoft have made available for tslint and at least a couple of rules (e.g. no-backbone-get-set-outside-model, jquery-deferred-must-complete) that do specific code analysis on how specific libraries are used.
I agree with Kitson Kelly (@kitsonk) on this issue. it is perfectly valid to ignore the result of a call. so unless we introduce new syntax that marks these functions, this looks like a style restriction for specific teams and does not apply for the general population.
so unless we introduce new syntax that marks these functions
This is exactly what I'm asking for.
I really don't believe that it's a style restriction, because it breaks logic, not appearance. And this error is quite common and the harm is obvious. Sometimes a lot of time must be spent to find out why object field doesn't contain a value which I've set to it. And this applies at least to all people who uses immutable data structures (not only ImmutableJS) in some way. There are other potential use-cases like Result<Ok,Err> type, which must be always used to handle potential error cases.
I still believe this is a style guideline more than a compiler error. i would assume some users want to opt out of the checking, or disable the error for some cases. this puts more in the vein of linting. i would assume something like #2900 to define the metadata on the function, and a linter to impose the rule.
This certainly got bounced around! I'm quite happy for this to be "just" a decorator and lint, but the --lib typings should be able to benefit from it too. Would declaration merging support adding design time decorators?
After having been burnt by a library updating a method to be const/pure in a new major version, whereas it modified state previously: +1
Calling pure methods without using the return value almost certainly is a bug. So if the library author was able to indicate this in some standardized way (e.g. the nodiscard as suggested above), TypeScript might've been able to prevent this issue from happening to me.
Reacted by stefnotch, gunnarmein-ts, Alexey Lebedev, Emilio, Anne Gropler, snarbies and Nagy Máté
This refactor is wrong; your buttons now no longer have any effect and your web app is broken due to what's logically a type error that TypeScript did not help prevent. The correct code has some added () function calls:
such decorator would also be useful if we want to return errors (similar to result in rust) instead of throwing errors. There was a similar request before, but it was closed since it is, supposedly, a duplicate of other issues: #38303
If the implementation of Result value must be used is like C++[[nodiscard]] (that is a function can be marked as nodiscard and also a type can be marked as nodiscard then all functions returning that are nodiscard) then this can be used for detecting floating promises. Promise need to have nodiscard by default.
Also ther is need to be an explicit way to discard values. May be:
A [[nodiscard]] as in C++ (callee-side) is not as useful as e.g. Haskell's -Wall compiler flag (caller-side-determined check):
It moves control to the writer of the function, instead of giving control over wardnings to the downstream project that uses them.
In C++, this makes [[nodiscard]] almost useless, as almost library code uses it. That introduces lots of bugs (e.g. ignored bool success return values). In contrast, those types of bugs do not exist in Haskell, where most users/projects choose to use -Wall to enable stricter warnings. It is also possible to turn these warnings into errors using -Werror, which can be useful in CI.
Further, a callee-side annotation would not fix key issues like my example (#8240 (comment)), as there the thing whose return value is ignored is itself a function call, and it's tough to find a place where you could put the annotation when partially applied functions are involved.
So I think a caller-side check that the user can enable as a warning is much better.
Even if a [[nodiscard]] callee-side feature was considered valuable, that should exist independent of caller-side warnings.
Niklas Hambüchen (@nh2) I'm a bit confused. Isn't [[nodiscard]] in C++ a callee-side annotation? Just trying to understand your argument on the proposed TS feature.
Hong Xu (@xuhdev) I would say he means that any callee side annotation - and he points out that feature is, in C++ - would be useless unless it gets retrofitted onto every library function that needs it, and chances of that are zero. That's why it is more practical to have a caller-side mechanism so that those of us who care about it can use it when calling all kinds of libraries and be safe.
I think it would be adopted. Developers of good software look forward to features like this which make their programs more resilient. It's like saying readonly is useless because no one uses it.
TypeScript also gives you escape hatches:
Define your own .d.ts with types
type MakeNodiscard<Fn extends (...args: unknown[]) => unknown> = Fn extends (...args: infer Args) => infer Result ? (...args: Args) => Nodiscard<Result> : never
Send a PR to @types
Reacted by Mike Hogye, NicoVIII, Fernando Mumbach, Gjum, vpzomtrrfrt, Andrés Correa Casablanca, Nagy Máté and Joseph Coppin
I would say he means that any callee side annotation - and he points out that feature is, in C++ - would be useless unless it gets retrofitted onto every library function that needs it, and chances of that are zero. That's why it is more practical to have a caller-side mechanism so that those of us who care about it can use it when calling all kinds of libraries and be safe.
And here's the source code if anyone wants to try to modify the rule so that it can be used for your own functions. I think eslint is flexible enough to read the jsdoc comments on a function and look for whatever tag you want to add, e.g. nodiscard.
Activity
SPY commentedon Apr 21, 2016
+1
kitsonk commentedon Apr 21, 2016
Why? Not using a return value is exactly what a linter is best at doing. Identifying non-syntatical errors that may lead to logic errors in the code.
SPY commentedon Apr 21, 2016
but linter does not have type information I suppose. so it will be very noisy about legal cases
s-panferov commentedon Apr 21, 2016
Yes, we must rely on type information here, but
tslintdoesn't have an access to it.kitsonk commentedon Apr 21, 2016
tslinthas access to all the information that the TypeScript compiler has when compiling code, because it integrates to the language services of TypeScript. For example there are several custom rules that teams have Microsoft have made available fortslintand at least a couple of rules (e.g.no-backbone-get-set-outside-model,jquery-deferred-must-complete) that do specific code analysis on how specific libraries are used.mhegazy commentedon Apr 21, 2016
I agree with Kitson Kelly (@kitsonk) on this issue. it is perfectly valid to ignore the result of a call. so unless we introduce new syntax that marks these functions, this looks like a style restriction for specific teams and does not apply for the general population.
s-panferov commentedon Apr 21, 2016
This is exactly what I'm asking for.
I really don't believe that it's a style restriction, because it breaks logic, not appearance. And this error is quite common and the harm is obvious. Sometimes a lot of time must be spent to find out why object field doesn't contain a value which I've set to it. And this applies at least to all people who uses immutable data structures (not only ImmutableJS) in some way. There are other potential use-cases like
Result<Ok,Err>type, which must be always used to handle potential error cases.s-panferov commentedon Apr 21, 2016
Mohamed Hegazy (@mhegazy), Kitson Kelly (@kitsonk)
The example you show doesn't use type information, it uses only syntax analysis:
https://github.com/Microsoft/tslint-microsoft-contrib/blob/master/src/jqueryDeferredMustCompleteRule.ts#L19-L30
In my example there is no any syntactical markers which can be used to find invalid usage, so we need information of type level.
I think it will try to write a formal proposal for this to explain what I'm taking about and why this is useful.
mhegazy commentedon Apr 21, 2016
I still believe this is a style guideline more than a compiler error. i would assume some users want to opt out of the checking, or disable the error for some cases. this puts more in the vein of linting. i would assume something like #2900 to define the metadata on the function, and a linter to impose the rule.
s-panferov commentedon Apr 21, 2016
Mohamed Hegazy (@mhegazy) this can really work with ambient decorators if
tslint:instanceOfObj.set('smth', 1)setis a functionsetof classObj.Objis declared.Is this possible with
tslint?mhegazy commentedon Apr 21, 2016
The compiler API exposes a checker object that can answer these questions, so i do not see why not.
17 remaining items
simonbuchan commentedon Jul 8, 2021
This certainly got bounced around! I'm quite happy for this to be "just" a decorator and lint, but the --lib typings should be able to benefit from it too. Would declaration merging support adding design time decorators?
abdulkareemnalband commentedon Jul 9, 2021
I think it should be implemented similar to c++
nodiscard, that bothTimWolla commentedon Aug 31, 2021
After having been burnt by a library updating a method to be const/pure in a new major version, whereas it modified state previously: +1
Calling pure methods without using the return value almost certainly is a bug. So if the library author was able to indicate this in some standardized way (e.g. the
nodiscardas suggested above), TypeScript might've been able to prevent this issue from happening to me.nh2 commentedon Sep 28, 2021
Here is another example of a refactoring error that this type of check would prevent:
Consider having an onclick handler:
used like:
Now you refactor, adding some logging:
This refactor is wrong; your buttons now no longer have any effect and your web app is broken due to what's logically a type error that TypeScript did not help prevent. The correct code has some added
()function calls:Here, one needed to call
f(someArg)()instead off(someArg); the latter returns a function that is then unused.If TypeScript had the ability to warn on unused return values, like Haskell and Rust can do with
-Wall, this mistake would be impossible to make.LordOfTheDings commentedon Sep 7, 2022
What's the current status on this?
gunnarmein-ts commentedon Nov 7, 2022
I agree that a "nodiscard" decorator would be very useful. As someone said above, these bugs a hard to find once someone put them in.
ns-vpanfilov commentedon Nov 18, 2022
such decorator would also be useful if we want to return errors (similar to
resultin rust) instead of throwing errors. There was a similar request before, but it was closed since it is, supposedly, a duplicate of other issues: #38303emilioplatzer commentedon Mar 7, 2023
If the implementation of
Result value must be usedis like C++[[nodiscard]](that is a function can be marked asnodiscardand also a type can be marked asnodiscardthen all functions returning that arenodiscard) then this can be used for detecting floating promises.Promiseneed to havenodiscardby default.Also ther is need to be an explicit way to discard values. May be:
then
see #13376
nh2 commentedon Mar 7, 2023
A
[[nodiscard]]as in C++ (callee-side) is not as useful as e.g. Haskell's-Wallcompiler flag (caller-side-determined check):It moves control to the writer of the function, instead of giving control over wardnings to the downstream project that uses them.
In C++, this makes
[[nodiscard]]almost useless, as almost library code uses it. That introduces lots of bugs (e.g. ignoredbool successreturn values). In contrast, those types of bugs do not exist in Haskell, where most users/projects choose to use-Wallto enable stricter warnings. It is also possible to turn these warnings into errors using-Werror, which can be useful in CI.Further, a callee-side annotation would not fix key issues like my example (#8240 (comment)), as there the thing whose return value is ignored is itself a function call, and it's tough to find a place where you could put the annotation when partially applied functions are involved.
So I think a caller-side check that the user can enable as a warning is much better.
Even if a
[[nodiscard]]callee-side feature was considered valuable, that should exist independent of caller-side warnings.xuhdev commentedon Feb 22, 2024
Niklas Hambüchen (@nh2) I'm a bit confused. Isn't
[[nodiscard]]in C++ a callee-side annotation? Just trying to understand your argument on the proposed TS feature.gunnarmein-ts commentedon Feb 22, 2024
Hong Xu (@xuhdev) I would say he means that any callee side annotation - and he points out that feature is, in C++ - would be useless unless it gets retrofitted onto every library function that needs it, and chances of that are zero. That's why it is more practical to have a caller-side mechanism so that those of us who care about it can use it when calling all kinds of libraries and be safe.
laverdet commentedon Feb 22, 2024
I think it would be adopted. Developers of good software look forward to features like this which make their programs more resilient. It's like saying
readonlyis useless because no one uses it.TypeScript also gives you escape hatches:
.d.tswith typestype MakeNodiscard<Fn extends (...args: unknown[]) => unknown> = Fn extends (...args: infer Args) => infer Result ? (...args: Args) => Nodiscard<Result> : never@typesnh2 commentedon Feb 23, 2024
This is exactly what I meant.
Tyler-Murphy commentedon Aug 14, 2024
This eslint rule might be useful: https://github.com/SonarSource/eslint-plugin-sonarjs/blob/master/docs/rules/no-ignored-return.md
And here's the source code if anyone wants to try to modify the rule so that it can be used for your own functions. I think eslint is flexible enough to read the jsdoc comments on a function and look for whatever tag you want to add, e.g.
nodiscard.