Validating Parentheses Input Using TypeScript

Abstract image used to represent Validating Parentheses Input Using TypeScript
Image by Mark Koenov.

The Valid Parentheses Problem is a common interviewtype problem but isn't just academic; it also arises fairly frequently in realworld development scenarios (although not always specifically in coupling parenthesis types), particularly when it comes to lowerlevel interpreters or linting tools.

By ensuring that all opened brackets, braces, and parentheses are properly closed in the correct order, you add another layer of robustness to your code.


Understanding the Problem

The task itself is straightforward: Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

  • Open brackets are closed by the same type of brackets. And...
  • Open brackets are closed in the correct order.

For example, {}, (), or [] are valid whilst (], [), or {[} would not be.

There is an additional layer of complication to this task in that nesting of parenthesis types should also be handled. It is valid to have one type nested inside another, as long as they open and close in the correct consecutive order.

Some Examples

Input: s = "()"
Output: true

Input: s = "(]"
Output: false

Input: s = "{[]}"
Output: true

Input: s = "{[}]"
Output: false


How We Can Solve It

The most straightforward way to tackle this problem is to use a stack data structure. In this context, a stack is a LastIn, FirstOut (LIFO) data structure where the last element added is the first one to be removed. You could visualise it as being akin to a stack of books; you can only add or remove a book from the top of the stack.

For matching opening and closing brackets, this data structure is a really natural fit. When you encounter an opening bracket, you 'push' it onto the stack, and when you encounter a closing bracket, you 'pop' the last element from the stack to see if they match. By using a stack, we can easily keep track of the brackets and validate them simply and efficiently.

In Code

Here's how you could implement this solution method using TypeScript:

const isValid = (s: string): boolean => {
  const stack: string[] = [];
  const map: { [key: string]: string } = {
    ')': '(',
    '}': '{',
    ']': '[',
  };

  for (const char of s) {
    if (['(', '{', '['].includes(char)) {
      stack.push(char);
    } else {
      const topElement = stack.pop();
      if (map[char] !== topElement) {
        return false;
      }
    }
  }

  return stack.length === 0;
};

How the Code Works

  1. We initialise an empty stack and a mapping of closing and opening brackets.
  2. We then iterate through the provided string.
  3. When an opening bracket is encountered, it is pushed onto the stack.
  4. When a closing bracket is encountered, we check if its corresponding opening bracket is at the top of the stack.
  5. At the end, if the stack is empty, the string is valid.

Alternative Solution Methods

There is another way to view the problem: keep removing adjacent matching pairs until either the string is empty or no pair remains. It is useful for comparison, but repeated scans and string creation can cost more than the single stack pass.

Repeated Adjacent‑Pair Removal

Remove adjacent (), [] and {} pairs, then repeat. Nested pairs become adjacent as their inner contents disappear. An empty final string is valid; a nonempty string with no removable pair is not. This can be written iteratively or recursively, and may take quadratic time on deeply nested input.

Why Counters are Not Enough

For a single bracket type, a counter can work: it must never go negative, and it must finish at zero. Separate counters for mixed bracket types lose nesting order. For example, ([)] balances each count but is invalid. We need to remember which opener is waiting to close.

Where Two Pointers Fit

I like the twopointer approach for problems such as valid palindromes and 3Sum Closest. Mixed nested brackets need a different invariant: comparing the outer positions does not tell us which opening bracket must close next. I would use the stack here.

The stack records that order directly. It visits the input once and needs space for the unmatched opening brackets, giving O(n) time and up to O(n) extra space.


Conclusion

The useful part of this exercise is recognising the order we need to preserve. A stack gives us the most recent opening bracket whenever a closing one arrives. Keep the mixednesting cases in the tests; merely counting each bracket type would miss them.


Want to find out more?

If you need senior handson support with a complex React or Next.js platform, migration, performance issue, or technical SEO problem, send me the context and I'll tell you where I can help.