Skip to content

RegExMatchArray.groups does not allow an undefined value #61476

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Closed
PartMan7 opened this issue Mar 24, 2025 · 7 comments
Closed

RegExMatchArray.groups does not allow an undefined value #61476

PartMan7 opened this issue Mar 24, 2025 · 7 comments

Comments

@PartMan7
Copy link

🔎 Search Terms

"regexp", "groups", "capture groups"

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about key types and object types.
  • I was unable to test this on prior versions to 4.0.5 because RegExpMatchArray.groups was added then.

⏯ Playground Link

https://www.typescriptlang.org/play/?target=99&ts=5.9.0-dev.20250324#code/MYewdgzgLgBAtgQysAFjAvDA5ArA6RZFACgHpiB+AHgBsBTKKOgJwD4BtBAWgC8BdAJQAfSlTABXOACMWrADoATAaQEBuAFABLAGYxihVBTwBzZiHEAHCAJgBvdTEcxQkWKfNWM8JKhNnLEDAIgbYw9IwsAFww0MyaYMaqMBLSUTDiYAp02vF0CjAAvjBCdmEMTMzRGVk5YHlJKTKVMVBxCYVJpKTOCGBYsAqazHTAUDQAnjBQ4xYjwVAOTi4QIPR4NCDGxO4BaouOOnpYjSxYMPHeRH4e1s7gK2sbWwYo1wF4J8zrdAlQKGowLrOZjBFB0CDqApAA

💻 Code

const match = 'a'.match(/(?<letter>[a-z])|(?<number>\d)/);
if (match?.groups) {
    const groups = match.groups as { letter: string; number: undefined } | { letter: undefined; number: string }; // can't directly typecast
    console.log(groups);
    if ('number' in match.groups) console.log(match.groups.number.length); // crashes without warning
}

🙁 Actual behavior

  • TypeScript has RegExpMatchArray.groups typed as { [key: string]: string } instead of potentially { [key: string]: string | undefined }, and does not allow typecasting it to an object with undefined as a value.

  • TypeScript does not allow casting Match.groups as an object whose keys may be undefined (eg: { group1: string; group2: undefined })
  • TypeScript assumes the in operator to narrow the type due to undefined not being a valid value, despite unmatched named groups in alternation in RegEx returning undefined.

🙂 Expected behavior

  • undefined should be allowed in the value type (for typecasting correctly or for correctly not narrowing with in).

Additional information about the issue

Checked #32098 but that looks at inferring types, not allowing casting.

@MartinJohns
Copy link
Contributor

Casting is allowed when you use a compatible type: { [group: string]: string | undefined }
You can't use your type with the known properties due to #32098 not being supported. Alternatively you can first cast to unknown, then to your custom type.

@PartMan7
Copy link
Author

I get why it happens, but it's incorrect from both standpoints (the value can be undefined even if the key is in the object, and I shouldn't need to cast to unknown when my type is a subset of the intended type). I understand that match.groups as { [group: string]: string | undefined } as { letter: string; number: undefined } | { letter: undefined; number: string } works, but that's just a workaround, not a solution (at least to me).

My issue here is TypeScript reporting the captured group type invalidly (ie; asserting that value will always be string even when it can also be undefined).

@MartinJohns
Copy link
Contributor

I shouldn't need to cast to unknown when my type is a subset of the intended type

But it isn't a subset, they're incompatible types. { letter: string; number: undefined } | { letter: undefined; number: string } is not a subset of { [key: string]: string }.

@jcalz
Copy link
Contributor

jcalz commented Mar 24, 2025

Duplicate #59147, #51292, #49228, #17053, etc

@PartMan7
Copy link
Author

I shouldn't need to cast to unknown when my type is a subset of the intended type

But it isn't a subset, they're incompatible types. { letter: string; number: undefined } | { letter: undefined; number: string } is not a subset of { [key: string]: string }.

You're speaking of the types in play here; I'm trying to say that TypeScript's provided type here is incorrect in the first place (and that the correct type would let this code work). Regardless, it seems I'd missed the closed issues that @jcalz mentioned and so this probably won't go anywhere. Thanks!

@MartinJohns
Copy link
Contributor

You're speaking of the types in play here; I'm trying to say that TypeScript's provided type here is incorrect in the first place (and that the correct type would let this code work).

.. which would first require #32098, as mentioned.

@PartMan7
Copy link
Author

(32098 deals with inferring the types from the given RegEx, which is something else entirely)

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants