Array.prototype.filter doesn't require callback to return
See original GitHub issueI dig a bit and found out in #5850 and subsequently in #7779 the signature of filter has been changed not to return a boolean, so that people could return a truthy value in the implementation of the filter.
While I undertand the convenience in terms of ease of use, this as far as I observed puts developers at risk of simple bugs like the following:
const even = [1,2,3].filter(x => {
x % 2 == 0
})
Notice the missing return
statement means the function returns undefined
, which translates to false
, which filters all the items out. This applies also in the case of noImplicitReturns
set to true, since the function return type is any
, thus it could be a void function for what the compiler knows.
I believe TypeScript’s job should be to put this kind of cases in check actually, so that things like truthy values (which sometimes aren’t as obvious as we might think and introduce bugs we don’t really understand) can be better taken care of.
One solution to make both parties happy would be to optionally support truthy values (maybe an extra compiler flag? Another truthy
type to allow for more relaxed checking?) and reintroduce the original signature of filter.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:9
- Comments:8 (2 by maintainers)
A
TruthyFalsy
type would also allow us to catch bugs inif
statements - we could add a compiler option forstrictIfConditions
or something, which requires the condition of anif
statement to be aboolean
orTruthyFalsy
type. For example:The bug here, of course, is that we should have done
if (isReady())
instead ofif (isReady)
. But currently typescript will not catch that bug, and the code will execute as ifisReady
is always true.Same for
&&
and||
operators - we could require both operands to beTruthyFalsy
.However, this could end up being overly restrictive. I often like to do
if (x)
to check ifx
is not undefined, which would be prohibited. I also like to doconst y = x || "default";
(using||
as sort of a null-coalescing operator), which would also be prohibited.As a workaround, we could allow
null
orundefined
types in addition toTruthyFalsy
andboolean
types. But this seems like a strange/unexpected exception to the rule. Alternatively, we could allow any type except function types, just to prevent this specific bug - then this becomes independent of theTruthyFalsy
type.I understood the
TruthyFalsy
type to be any type that has at least one truthy and one falsy value. A{ foo: number } | null
obviously matches. A boolean, string or number also do because they inherently have falsy values. However, neither{ foo: 3 }
, nor() => {}
, nor"string-literal"
would match because they are always truthy. Neitherundefined
, nornull
, because they are always falsy. We then need an exception to make literaltrue
orfalse
matchTruthyFalsy
. This will ensure that we can have “always true” or “always false” filters as well as use constant values in conditions:const DEBUG = false; if (DEBUG) { ... }
.@aj-r, you say it could be overly restrictive. I don’t understand. What am I missing in your examples?
If
x
is{ foo: number } | undefined
, then it would be allowed, that matchesTruthyFalsy
, it would not be prohibited. Ifx
is always truthy or always undefined, then the condition is meaningless, and it’s great that it’s prohibited, no?Again, if
x
can be truthy and falsy, this would not be prohibited. If it’s always truthy or always falsy, the code above is a programmer error, so it’s good it’s prohibited.