Hey there! π
I'm back with another installment of Find the bug, this time with Typescript/Javascript. Regular expressions are useful, but can behave in some unexpected ways. Can you tell me what the code below will output and what the cause for it is?
!! Don't look at the comments to prevent spoilers if you want to solve it by yourself !!
Buggy code
const TEST_REGEXP = /[a-z0-9]+_[a-z0-9]+/gi;
function isValidName(value) {
if (typeof value !== 'string') return false;
return TEST_REGEXP.test(value);
}
const filenames = [
"test_1",
"test_1",
"test_2",
"other_test",
"some_file"
];
for (let name of filenames) {
console.log(isValidName(name));
}
Top comments (24)
The regex instance keeps track of the lastIndex and therefore does not for all items start from zero. You can remove the g flag and instead use string start and end anchors
/^[a-z0-9]+_[a-z0-9]+$/i
so the regex won't keep the lastIndex. Or you reset the lastIndex before testisValidName = (value) => { TEST_REGEXP.lastIndex = 0; return TEST_REGEXP.test(value); }
Here I was, looking for an error in the actual regex xD
I was not even aware of this weird behaviour, and would probably have written it as
instead, and anchored the regex to beginning and end, of course :D
And for the actual answer:
Fantastic, thanks for the solutions! I'd go with removing the
g
lobal flag . Though the other solution is interesting, did not think of reseting lastIndex manually!Got it! Because
.test()
tests the rest of the string when the g flag is enabled!Just remove the g flag to fix
There you go, you found it!! Kinda weird if you don't expect that behaviour right?
It is! I never knew that till today!
Nice that's wonderful! That's what I aim this series to be, so I'm glad you learned something new!
I also didn't know this. It's probably very useful when building a parser, but when you don't expect it, it's a super confusing error π
Yup, I was super confused when I first encountered this error. I found it making some unit tests for work, and lost a bit of time trying to figure it out... just for the issue to be a silly little flag xD
Using the
g
(global) flag with.exec
stores the index up to which the pattern matches. If the pattern does not match, the index is reset to 0.If the previous search was successful, the next search starts after the stored index, even though the string is different. This is because the regular expression stores the matched index. I encountered this bug while writing an input validator. Took me half a day to figure this out. π π
Here is the explanation from the docs
So for the above code:
Thus the output is:
Great breakdown, thanks for taking the time to explain it in such detail!!
Tricky. Just needed to remove the
g
. Now it's aReExp
.2nd bug no-one has mentioned yet: the regex matches anywhere in the string, so for example,
isValidName('ε½ε½ε³ε³ o_O εεεεεε')
istrue
, because theo_O
in the middle matches.Good catch, that's my mistake. When simplifying the example for this post I must've missed that.
Great riddle!
A little note on the typescript in this file, it might as well not be here because it's better to state what is expected then guard against it rather than fall through to runtime
Yup, good point, my bad. I found this bug on a typescript project so I though to include it as TS, but I simplified the example so much that it does not make sense to have it now!
I will fix the type to make it more relevant (I've removed the any type actually).
false
false
false
true
true
π€·ββοΈ
Good try, but no, the result is weirder!!! π€
just remove the g flag
Nice! There you go
I'm really close... But can't seem to get it! (Don't tell me)
I won't π