Home > Software engineering >  How do you destructure named capture groups?
How do you destructure named capture groups?

Time:04-24

In JavaScript, using named capture groups is pretty convenient:

const auth = 'Bearer AUTHORIZATION_TOKEN'
const { groups: { token } } = /Bearer (?<token>[^ $]*)/.exec(auth)
console.log(token) // "AUTHORIZATION_TOKEN"

When I try that in typescript, it doesn't compile because groups could be null. If I put a ! after exec(...), it just kicks the can: token could be undefined.

In typescript, is there any way I can destructure the regex like above?

CodePudding user response:

You can also use a direct assignment to a token variable:

const auth = 'Bearer AUTHORIZATION_TOKEN'
const token = /Bearer (?<token>\S )/.exec(auth)?.groups?.token
console.log(token); // => "AUTHORIZATION_TOKEN"

Since you know the group name, you can refer to the group?.token match value.

Also, note (?<token>\S ) captures one or more non-whitespace chars.

CodePudding user response:

It doesn't compile because groups could be null.

No, it doesn't compile because .exec() can return null, when the regex does not match. Attempting to access a property like .groups on that will cause a TypeError: Cannot read properties of null.

You'll need a fallback value (using nullish coalescing and default initialisers) to destructure in that case:

const { groups: {token} = {} } = /Bearer (?<token>[^ $]*)/.exec(auth) ?? {}

or simpler with optional chaining:

const { token } = /Bearer (?<token>[^ $]*)/.exec(auth)?.groups ?? {}

CodePudding user response:

UPDATE: Bergi's answer is the more correct and typesafe way to accomplish this.


It's ugly, but something like this would work:

const auth = 'Bearer AUTHORIZATION_TOKEN'
type RegexTokenObj = { groups: { token: string } };
const { groups: { token } } = (/Bearer (?<token>[^ $]*)/.exec(auth) as unknown as RegexTokenObj)
console.log(token) // "AUTHORIZATION_TOKEN"

If you just try const { groups: { token } } = (/Bearer (?<token>[^ $]*)/.exec(auth) as RegexTokenObj) it will yell at you that "Conversion of type 'RegExpExecArray | null' to type 'RegexTokenObj' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first." Thus we can convert to unknown before casting as the type we know/expect it will be. That said, we are overriding the type system here, so we should be certain we are confident that we are correct in doing so, so be careful.

  • Related