JavaScript isWellFormed and toWellFormed Explained

JavaScript strings use UTF-16 encoding, which occasionally results in malformed strings containing unpaired surrogate code units. The String.prototype.isWellFormed() and String.prototype.toWellFormed() methods provide a native, efficient way to check for and fix these ill-formed surrogate pairs. This article covers why these methods exist, how they function, and how to use them to prevent runtime errors in modern JavaScript applications.

The Problem with Lone Surrogates in JavaScript

In UTF-16, characters outside the Basic Multilingual Plane (such as emojis or rare symbols) are represented using a pair of 16-bit code units known as a surrogate pair: a lead (high) surrogate followed by a trail (low) surrogate.

If a string contains a lead surrogate without a corresponding trail surrogate—or vice versa—it is considered “ill-formed.” Passing an ill-formed string to certain Web APIs (such as encodeURI() or crypto.subtle.digest()) throws a URIError or causes unexpected encoding behavior.

String.prototype.isWellFormed()

The isWellFormed() method returns a boolean indicating whether a string is free of lone surrogates.

// Well-formed strings
const text = "Hello, world!";
const emoji = "🚀"; // Valid surrogate pair: \uD83D\uDE80

console.log(text.isWellFormed());  // true
console.log(emoji.isWellFormed()); // true

// Ill-formed string with a lone surrogate
const loneSurrogate = "\uD83D"; // High surrogate without low surrogate

console.log(loneSurrogate.isWellFormed()); // false

String.prototype.toWellFormed()

The toWellFormed() method returns a new string where all lone surrogates are replaced with the Unicode replacement character U+FFFD (``). If the original string is already well-formed, it returns a direct copy without modifications.

const badString = "User: \uD83D (invalid)";
const cleanString = badString.toWellFormed();

console.log(cleanString); // "User:  (invalid)"
console.log(cleanString.isWellFormed()); // true

Common Use Cases

  1. Preventing encodeURI Errors: Functions like encodeURI() fail immediately when encountering lone surrogates. Checking or sanitizing strings beforehand avoids uncaught exceptions.
function safeEncode(url) {
  return encodeURI(url.toWellFormed());
}
  1. Sanitizing Data for External APIs: Many database drivers and foreign function interfaces (FFIs) expect strict UTF-8 text. Using toWellFormed() guarantees that data sent over the network or saved to disk conforms to Unicode standards.