Regex-validated strings in Typescript: an implementation

Viewed 108

Regex-validated strings are not a thing in Typescript.

Still, the need is here: in my case, immutable strings that are validated against a Regex for UUIDv4, URIs, Email, etc.

I've got something close but I'm not happy with it, I have to rewrite the regex access and test function every time.

Here's what I have so far:

An abstract class "ValidatedString"

export interface Predicate<T> { (x: T): boolean }
export abstract class ValidatedString {
  #str: string

  constructor(str: string, predicate: Predicate<string>, className?: string) {
    if (!predicate(str)) throw new Error(`Invalid ${className ? className : 'format'}: ${str}`)
    this.#str = str
  }

  get length() { return this.#str.length }

  toString = () => this.#str
}

And classes that implement it:

import { v4 } from 'uuid'

/** Regex-validated string class for UUIDv4 */
export class UUIDv4 extends ValidatedString {
  constructor(str: string) { super(str, UUIDv4.test, 'UUID v4') }

  static readonly regex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i

  /** Test if a string matches the class predicate */
  static test = (str: string) => UUIDv4.regex.test(str)

  /** Generate a random string that matches the class predicate */
  static generate = () => new UUIDv4(v4())
}

/** Regex-validated string class for UUIDv4 */
export class ApiVersion extends ValidatedString {
  constructor(str: string) { super(str, ApiVersion.test, 'API version') }

  static readonly regex = /^[0-9]+\.[0-9]+(\.[0-9]+)?[a-z]*$/

  /** Test if a string matches the class predicate */
  static test = (str: string) => ApiVersion.regex.test(str)
}

Despite my efforts to eliminate the obvious redundancies between the inheriting classes, I have failed: one cannot use a protected static field in the abstract class, so I'm left with rewriting the test function and the regex access every time. I can't offer a generic signature for the generate function either.

I would like to basically build UUIDv4 as a ValidatedString with UUIDv4 regex and offer a regex access,test method and optional generate function signature. Do you see how to implement this properly? Is there a way to build a type from a static parameter such as a Regex literal?

0 Answers
Related