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
regexp parameter of String.prototype.matchAll is too restrictive #47310
Comments
Looks like same as #37211 and other issues where TS doesn't account for 'implicit conversion' All these "perfectly valid" and "work as expected" [...'123null123object123jest'.matchAll(123)].flat()
// (3) ['123', '123', '123']
[...'123null123object123jest'.matchAll(null)].flat()
// ['null']
[...'123null123object123jest'.matchAll({})].flat()
// (9) ['o', 'b', 'j', 'e', 'c', 't', 'j', 'e', 't'] |
Except that |
Implicit conversion of string to regexp is a source of many bugs and security vulnerabilities. If the string is properly sanitized and escaped as to make a suitable regexp, why not show that explicitly in your code with |
Because it increases code complexity. I see your point, though; I'm running into this in a unittest where the string is defined right above the call to |
I think fundamentally, I'm expecting TS to just check that types are compatible. Apparently, the intention of TS is to go beyond that and also raise hard errors when compatible types are potentially risky, like a very strict linter. It's a choice I don't like, but of course a project cannot cater to everyone. Still, assuming that the intention is indeed to behave like a strict linter, I don't understand why |
Because that's not a source of common errors. Implicitly accepting arbitrary strings as regular expressions is. |
If the This code, for example, smells to high heaven. I wouldn't allow this anywhere. const searchString = 'The quick brown fox_hound jumps over the lazy dog.';
const query = 'fox.hound'; // Shouldn't be found, because that is not a substring, idk?
for (let match of searchString.matchAll(query)) {
console.log(match);
} |
That's an interesting claim. I can easily think of scenarios where adding a string to a number produces @RyanCavanaugh Is the frequency of the type of error a criterion for deciding whether to check only for compatibility, or also for advisibility? |
I'm not quite sure what you mean by that question. |
Compare the following two snippets of code, both of which are type-compatible according to the ES specification, but inadvisable from an engineering point of view (because they are likely to lead to problems). // snippet 1 (adding a string to a number)
1 + '2'
// snippet 2 (coercing a string to regex)
searchString.matchAll('fox')
Disregarding for a moment whether this is actually true, my question to you is the following: is the frequency, by which a compatible-but-inadvisable construct leads to problems, a criterion by which the TS team decides whether to raise an error? |
Thanks, that's a useful framing. There are indeed a large number of coercions that occur in normal JS that aren't problematic -- even It is ultimately rather subjective in a lot of places, and often requires attention to edge cases which some people consider to be outside the domain of their programs. For example, |
Right. I agree it's subjective. What is your stance towards string-to-number addition in particular? |
|
When TS was introduced, template strings were not a thing, so code like |
It’s been my observation that whether or not constructs like these are flagged as errors pretty much comes down to asking: “If someone writes this code in a real program, is it more likely to be a mistake or intentional—and in the latter case, how likely is it to do what the programmer expects?” It’s quite common (especially pre-template strings) to write things like It’s all pretty much driven by a heavy dose of pragmatism. TypeScript is a lot less “religious” than most typed languages because it’s designed to model idiomatic JavaScript. |
Also worth noting is that JS doesn’t really have “undefined behavior” as such - all behavior is well-defined by the specification, even the surprising stuff. But clearly TypeScript can’t allow “anything that won’t throw an error at runtime”, or it would be basically useless. So the line between intentional vs. unintentional coercions can become rather blurry sometimes. |
Thanks for making me aware of that. I thought it would result in a number due to the first operand, probably because I was confusing JS with another language. Given that it results in a string, I agree it is less problematic than a string-to-regex coercion.
Just for nuance, I'll mention what was going in my unittest. I was iterating over the keys of a small object and checking how often they occurred in a longer string. Due to the iteration, each key was assigned to a variable in turn, so literal regex notation was not an option. The object was a literal just a few lines up, and all keys contained only characters from the alphabet, so given that const testString = 'banana';
_.each({ a: 3, b: 1, n: 2 }, function(count, character) {
expect(Array.from(testString.matchAll(character)).length).toBe(count);
}); I learned a couple of things, most importantly what TS is aiming for (i.e., more than just checking for type compatibility). Thanks @RyanCavanaugh and @MartinJohns for taking the time to discuss this with me. |
Bug Report
This issue is somewhat related to, though not the same as, #36788 (same standard library method).
🔎 Search Terms
matchAll
🕗 Version & Regression Information
⏯ Playground Link
Playground link with relevant code
💻 Code
🙁 Actual behavior
The above code is perfectly valid, as
String.prototype.matchAll
implicitly converts its argument toRegExp
. The above playground even works as intended when you click Run. However, the TS type checker rejects the code on the grounds ofquery
not being aRegExp
:🙂 Expected behavior
Passing a string to
String.prototype.matchAll
should typecheck.The text was updated successfully, but these errors were encountered: