# String

The `String` module gives you Effect-style helpers for working with plain
JavaScript strings. There is no wrapper type — a `String` value is just a
`string` — but the functions are **data-last** (curried) so they drop straight
into `pipe` and `flow`, and several of them preserve string **literal types** at
the type level (`Capitalize`, `Trim`, `Concat`).

Operations that can _miss_ — `at`, `indexOf`, `match`, `search`, and friends —
return an [`Option`](https://effect.plants.sh/data-types/option/) instead of the native sentinels
(`-1`, `undefined`, `null`), so a "not found" result is a value you handle
explicitly rather than a magic number you have to remember to check.

```ts
import { pipe, String } from "effect"

// Data-last helpers chain cleanly in a pipe
const tags = pipe(
  "  TypeScript, Effect, Schema  ",
  String.trim,           // "TypeScript, Effect, Schema"
  String.toLowerCase,    // "typescript, effect, schema"
  String.split(", ")     // ["typescript", "effect", "schema"]
)
console.log(tags) // ["typescript", "effect", "schema"]
```

Because the helpers are curried, the single-argument call (`String.split(", ")`)
returns a function `(self: string) => ...`, which is exactly what `pipe` feeds
the previous result into. Every helper also has a data-first overload where it
makes sense, so `String.split("a,b", ",")` works too.

## Literal types are preserved

A handful of transforms thread the literal type through, so the compiler knows
the exact resulting string when the input is a literal:

```ts
import { String } from "effect"

const tag = String.capitalize("button") // type: "Button", value: "Button"
const trimmed = String.trim("  hi  ")    // type: "hi",     value: "hi"

// Type-level only, mirroring the runtime functions:
type T1 = String.Concat<"foo", "bar"> // "foobar"
type T2 = String.Trim<"  hi  ">        // "hi"
type T3 = Capitalize<"button">          // built-in TS intrinsic, same idea
```

## Basics: the namespace and the empty string

`String.String` is literally the global `String` constructor, re-exported so you
can reach it without leaving the Effect namespace, and `String.empty` is the
canonical `""`.

```ts
import { String } from "effect"

console.log(String.String === globalThis.String) // true
console.log(String.String(123))                  // "123" (native coercion)

console.log(String.empty)                  // ""
console.log(String.isEmpty(String.empty))  // true
```

---

# Reference

Every public export of the module, grouped by task. All examples are runnable.

## Constants, instances & guards

### `String`

The native `globalThis.String` constructor, re-exported. `String.String(value)`
performs native string coercion; `new String.String(value)` builds a boxed
object (rarely what you want).

```ts
import { String } from "effect"

console.log(String.String(true)) // => "true"
console.log(String.String([1, 2])) // => "1,2"
```

### `empty`

The empty string `""`, typed as the literal `""`.

```ts
import { String } from "effect"

console.log(String.empty) // => ""
```

### `isString`

A `Refinement<unknown, string>` that narrows unknown input to `string`.

```ts
import { String } from "effect"

console.log(String.isString("a")) // => true
console.log(String.isString(1)) // => false
```

### `Order`

An [`Order<string>`](https://effect.plants.sh/traits/order/) using lexicographic comparison. Returns
`-1`, `0`, or `1`.

```ts
import { String } from "effect"

console.log(String.Order("apple", "banana")) // => -1
console.log(String.Order("banana", "apple")) // => 1
console.log(String.Order("apple", "apple")) // => 0
```

### `Equivalence`

An [`Equivalence<string>`](https://effect.plants.sh/traits/equivalence/) using strict `===` equality.

```ts
import { String } from "effect"

console.log(String.Equivalence("hello", "hello")) // => true
console.log(String.Equivalence("hello", "world")) // => false
```

### `ReducerConcat`

A `Reducer<string>` that concatenates strings, starting from `""`. Use it with
APIs that consume a `Reducer` to fold many strings into one.

```ts
import { String } from "effect"

console.log(String.ReducerConcat.combineAll(["a", "b", "c"])) // => "abc"
console.log(String.ReducerConcat.combineAll([])) // => ""
```

## Concatenation & length

### `concat`

Concatenates two strings. The type-level `Concat<A, B>` produces the joined
literal type when both inputs are literals.

```ts
import { pipe, String } from "effect"

console.log(String.concat("hello", "world")) // => "helloworld"
console.log(pipe("hello", String.concat("world"))) // => "helloworld"

// Type-level equivalent:
type R = String.Concat<"hello", "world"> // "helloworld"
```

### `length`

The JavaScript string length, measured in UTF-16 code units (not user-perceived
characters).

```ts
import { String } from "effect"

console.log(String.length("abc")) // => 3
```

### `isEmpty`

Returns `true` for `""`. Narrows the type to `""`.

```ts
import { String } from "effect"

console.log(String.isEmpty("")) // => true
console.log(String.isEmpty("a")) // => false
```

### `isNonEmpty`

Returns `true` when the string has at least one code unit.

```ts
import { String } from "effect"

console.log(String.isNonEmpty("")) // => false
console.log(String.isNonEmpty("a")) // => true
```

## Case transforms (type-preserving)

These thread the literal type through using TypeScript's intrinsic string types,
so `toUpperCase("ab")` has type `"AB"`.

### `toUpperCase`

Uppercases the whole string; result type is `Uppercase<S>`.

```ts
import { pipe, String } from "effect"

console.log(String.toUpperCase("hello")) // => "HELLO"
console.log(pipe("a", String.toUpperCase)) // => "A"
```

### `toLowerCase`

Lowercases the whole string; result type is `Lowercase<S>`.

```ts
import { String } from "effect"

console.log(String.toLowerCase("HELLO")) // => "hello"
```

### `capitalize`

Uppercases the first character only; result type is `Capitalize<S>`.

```ts
import { String } from "effect"

console.log(String.capitalize("hello")) // => "Hello"
```

### `uncapitalize`

Lowercases the first character only; result type is `Uncapitalize<S>`.

```ts
import { String } from "effect"

console.log(String.uncapitalize("Hello")) // => "hello"
```

### `toLocaleLowerCase`

Lowercases according to a locale (handles cases the locale-agnostic version
gets wrong, e.g. Turkish dotted/dotless `i`).

```ts
import { pipe, String } from "effect"

console.log(pipe("İ", String.toLocaleLowerCase("tr"))) // => "i"
```

### `toLocaleUpperCase`

Uppercases according to a locale.

```ts
import { pipe, String } from "effect"

console.log(pipe("i̇", String.toLocaleUpperCase("lt-LT"))) // => "I"
```

## Trimming (type-level `Trim`)

The runtime trims behave like native methods; the `Trim`, `TrimStart`, and
`TrimEnd` type-level helpers compute the trimmed literal at compile time.

### `trim`

Removes whitespace from both ends. Result type is `Trim<A>`.

```ts
import { String } from "effect"

console.log(String.trim("  hello world  ")) // => "hello world"

type R = String.Trim<"  hi  "> // "hi"
```

### `trimStart`

Removes leading whitespace. Result type is `TrimStart<A>`.

```ts
import { String } from "effect"

console.log(String.trimStart("  hello world")) // => "hello world"
console.log(String.trimStart(" a ")) // => "a "
```

### `trimEnd`

Removes trailing whitespace. Result type is `TrimEnd<A>`.

```ts
import { String } from "effect"

console.log(String.trimEnd("hello world  ")) // => "hello world"
console.log(String.trimEnd(" a ")) // => " a"
```

## Slicing & indexing

### `slice`

Extracts a section between two indices (negative indices count from the end).

```ts
import { pipe, String } from "effect"

console.log(pipe("abcd", String.slice(1, 3))) // => "bc"
console.log(pipe("hello world", String.slice(0, 5))) // => "hello"
console.log(pipe("abcd", String.slice(-2))) // => "cd"
```

### `substring`

Extracts characters between `start` and (optional) `end`. Unlike `slice`,
negative arguments are treated as `0`.

```ts
import { pipe, String } from "effect"

console.log(pipe("abcd", String.substring(1))) // => "bcd"
console.log(pipe("abcd", String.substring(1, 3))) // => "bc"
```

### `at`

The character at a (possibly negative) index, as `Option`. `None` when out of
bounds — no `undefined` to forget about.

```ts
import { pipe, String } from "effect"

console.log(pipe("abc", String.at(1))) // => Option.some("b")
console.log(pipe("abc", String.at(-1))) // => Option.some("c")
console.log(pipe("abc", String.at(4))) // => Option.none()
```

### `charAt`

The character at a non-negative index, as `Option`. `None` when out of bounds.

```ts
import { pipe, String } from "effect"

console.log(pipe("abc", String.charAt(1))) // => Option.some("b")
console.log(pipe("abc", String.charAt(4))) // => Option.none()
```

### `charCodeAt`

The UTF-16 code unit at an index, as `Option<number>`. `None` instead of `NaN`
when out of bounds.

```ts
import { String } from "effect"

console.log(String.charCodeAt("abc", 1)) // => Option.some(98)
console.log(String.charCodeAt("abc", 4)) // => Option.none()
```

### `codePointAt`

The Unicode code point at an index, as `Option<number>`. `None` instead of
`undefined` when out of bounds.

```ts
import { pipe, String } from "effect"

console.log(pipe("abc", String.codePointAt(1))) // => Option.some(98)
console.log(pipe("abc", String.codePointAt(10))) // => Option.none()
```

### `takeLeft`

Keeps the first `n` characters. Clamps: `n <= 0` gives `""`, oversized `n` gives
the whole string, floats round down.

```ts
import { String } from "effect"

console.log(String.takeLeft("Hello World", 5)) // => "Hello"
console.log(String.takeLeft("Hi", 100)) // => "Hi"
```

### `takeRight`

Keeps the last `n` characters, with the same clamping rules as `takeLeft`.

```ts
import { String } from "effect"

console.log(String.takeRight("Hello World", 5)) // => "World"
console.log(String.takeRight("Hi", 0)) // => ""
```

## Search & test

Predicates (`includes`, `startsWith`, `endsWith`) return plain `boolean`; the
positional searches return `Option` so a miss is never `-1` or `null`.

### `includes`

`true` if `searchString` appears at or after the optional `position`.

```ts
import { pipe, String } from "effect"

console.log(pipe("hello world", String.includes("world"))) // => true
console.log(pipe("hello world", String.includes("foo"))) // => false
```

### `startsWith`

`true` if the string starts with the search string (optionally from a position).

```ts
import { pipe, String } from "effect"

console.log(pipe("hello world", String.startsWith("hello"))) // => true
console.log(pipe("hello world", String.startsWith("world"))) // => false
```

### `endsWith`

`true` if the string ends with the search string (optionally up to a position).

```ts
import { pipe, String } from "effect"

console.log(pipe("hello world", String.endsWith("world"))) // => true
console.log(pipe("hello world", String.endsWith("hello"))) // => false
```

### `indexOf`

Index of the first occurrence, as `Option<number>` — `None` instead of `-1`.

```ts
import { pipe, String } from "effect"

console.log(pipe("abbbc", String.indexOf("b"))) // => Option.some(1)
console.log(pipe("abbbc", String.indexOf("z"))) // => Option.none()
```

### `lastIndexOf`

Index of the last occurrence, as `Option<number>` — `None` instead of `-1`.

```ts
import { pipe, String } from "effect"

console.log(pipe("abbbc", String.lastIndexOf("b"))) // => Option.some(3)
console.log(pipe("abbbc", String.lastIndexOf("d"))) // => Option.none()
```

### `search`

Index of the first match for a string or `RegExp`, as `Option<number>` — `None`
instead of `-1`.

```ts
import { String } from "effect"

console.log(String.search("ababb", "b")) // => Option.some(1)
console.log(String.search("ababb", /abb/)) // => Option.some(2)
console.log(String.search("ababb", "d")) // => Option.none()
```

### `match`

Matches against a pattern, returning `Option<RegExpMatchArray>` — `None` instead
of `null` when there is no match.

```ts
import { Option, pipe, String } from "effect"

const m = pipe("hello", String.match(/l+/))
if (Option.isSome(m)) {
  console.log(`${m.value[0]}@${m.value.index}`) // => "ll@2"
}
console.log(Option.isNone(pipe("hello", String.match(/x/)))) // => true
```

### `matchAll`

Returns an iterator over all matches for a **global** `RegExp`, using native
`matchAll` semantics.

```ts
import { pipe, String } from "effect"

const matches = pipe("hello world", String.matchAll(/l/g))
console.log(
  Array.from(matches, (match) => `${match[0]}@${match.index}`).join(", ")
) // => "l@2, l@3, l@9"
```

### `localeCompare`

Locale-aware comparison returning an `Ordering` (`-1`, `0`, or `1`), with
optional locales and `Intl.CollatorOptions`.

```ts
import { pipe, String } from "effect"

console.log(pipe("a", String.localeCompare("b"))) // => -1
console.log(pipe("b", String.localeCompare("a"))) // => 1
console.log(pipe("a", String.localeCompare("a"))) // => 0
```

## Replace, split & build

### `replace`

Replaces the **first** match (string or non-global `RegExp`); a global `RegExp`
replaces every match. Follows native `String.prototype.replace`.

```ts
import { pipe, String } from "effect"

console.log(pipe("abc", String.replace("b", "d"))) // => "adc"
console.log(pipe("a-a-a", String.replace("-", "_"))) // => "a_a-a"
console.log(pipe("a-a-a", String.replace(/-/g, "_"))) // => "a_a_a"
```

### `replaceAll`

Replaces **all** occurrences of a substring or pattern.

```ts
import { pipe, String } from "effect"

console.log(pipe("ababb", String.replaceAll("b", "c"))) // => "acacc"
console.log(pipe("ababb", String.replaceAll(/ba/g, "cc"))) // => "accbb"
```

### `split`

Splits into a **non-empty** array of substrings. Splitting `""` yields `[""]`,
so the result is always at least one element.

```ts
import { pipe, String } from "effect"

console.log(String.split("hello,world", ",")) // => ["hello", "world"]
console.log(pipe("abc", String.split(""))) // => ["a", "b", "c"]
console.log(pipe("", String.split(""))) // => [""]
```

### `repeat`

Repeats the string `count` times.

```ts
import { pipe, String } from "effect"

console.log(pipe("a", String.repeat(5))) // => "aaaaa"
console.log(pipe("ab", String.repeat(3))) // => "ababab"
```

### `padStart`

Pads from the start to `maxLength` with an optional fill string (default `" "`).

```ts
import { pipe, String } from "effect"

console.log(pipe("a", String.padStart(5))) // => "    a"
console.log(pipe("7", String.padStart(3, "0"))) // => "007"
```

### `padEnd`

Pads from the end to `maxLength` with an optional fill string (default `" "`).

```ts
import { pipe, String } from "effect"

console.log(pipe("a", String.padEnd(5))) // => "a    "
console.log(pipe("a", String.padEnd(5, "_"))) // => "a____"
```

### `normalize`

Normalizes to a Unicode normalization form: `"NFC"` (default), `"NFD"`,
`"NFKC"`, or `"NFKD"`. Use it before comparing strings that may use combining
characters.

```ts
import { pipe, String } from "effect"

const str = "ẛ̣"
console.log(pipe(str, String.normalize("NFC")) === str) // => true
console.log(pipe(str, String.normalize("NFKC"))) // => "ṩ"
```

## Lines

### `linesIterator`

An iterable over each line, **stripping** the trailing newline. Handles `\n`,
`\r`, and `\r\n`.

```ts
import { String } from "effect"

console.log(Array.from(String.linesIterator("hello\nworld\n")))
// => ["hello", "world"]
```

### `linesWithSeparators`

An iterable over each line, **keeping** the trailing newline characters.

```ts
import { String } from "effect"

console.log(Array.from(String.linesWithSeparators("hello\nworld\n")))
// => ["hello\n", "world\n"]
```

### `stripMargin`

Strips a leading `|` margin from every line (after any leading whitespace).
Handy for multi-line template literals.

```ts
import { String } from "effect"

console.log(String.stripMargin("  |hello\n  |world"))
// => "hello\nworld"
```

### `stripMarginWith`

Like `stripMargin`, but you choose the margin character.

```ts
import { String } from "effect"

console.log(String.stripMarginWith("  $hello\n  $world", "$"))
// => "hello\nworld"
```

## Case-style conversions

There are two families here. The **fixed converters** (`snakeToCamel`,
`camelToSnake`, …) assume their input already follows the named source shape and
are cheap, direct transforms. The **general converters** (`camelCase`,
`snakeCase`, `kebabCase`, `pascalCase`, `constantCase`, and the configurable
`noCase`) tokenize arbitrary mixed input — spaces, separators, and case
boundaries — so they are the right choice for free-form text.

### `snakeToCamel`

`snake_case` to `camelCase`. Assumes snake_case input.

```ts
import { String } from "effect"

console.log(String.snakeToCamel("hello_world")) // => "helloWorld"
console.log(String.snakeToCamel("foo_bar_baz")) // => "fooBarBaz"
```

### `snakeToPascal`

`snake_case` to `PascalCase`.

```ts
import { String } from "effect"

console.log(String.snakeToPascal("hello_world")) // => "HelloWorld"
console.log(String.snakeToPascal("foo_bar_baz")) // => "FooBarBaz"
```

### `snakeToKebab`

`snake_case` to `kebab-case` (swaps `_` for `-`).

```ts
import { String } from "effect"

console.log(String.snakeToKebab("hello_world")) // => "hello-world"
console.log(String.snakeToKebab("foo_bar_baz")) // => "foo-bar-baz"
```

### `camelToSnake`

`camelCase` to `snake_case`.

```ts
import { String } from "effect"

console.log(String.camelToSnake("helloWorld")) // => "hello_world"
console.log(String.camelToSnake("fooBarBaz")) // => "foo_bar_baz"
```

### `pascalToSnake`

`PascalCase` to `snake_case`.

```ts
import { String } from "effect"

console.log(String.pascalToSnake("HelloWorld")) // => "hello_world"
console.log(String.pascalToSnake("FooBarBaz")) // => "foo_bar_baz"
```

### `kebabToSnake`

`kebab-case` to `snake_case` (swaps `-` for `_`).

```ts
import { String } from "effect"

console.log(String.kebabToSnake("hello-world")) // => "hello_world"
console.log(String.kebabToSnake("foo-bar-baz")) // => "foo_bar_baz"
```

### `noCase`

The general normalizer the others are built on: splits input into word parts at
case boundaries and non-word characters, transforms each part (default
`toLowerCase`), and joins with a delimiter (default `" "`). Options:
`splitRegExp`, `stripRegExp`, `delimiter`, and `transform`.

```ts
import { String } from "effect"

console.log(String.noCase("HelloWorld")) // => "hello world"
console.log(String.noCase("user_profile-ID")) // => "user profile id"

// Custom delimiter + transform → dot.case
console.log(
  String.noCase("HelloWorld", {
    delimiter: ".",
    transform: String.toLowerCase
  })
) // => "hello.world"
```

### `camelCase`

Normalizes free-form input into lower-initial `camelCase`.

```ts
import { String } from "effect"

console.log(String.camelCase("Hello world")) // => "helloWorld"
console.log(String.camelCase("user_profile_id")) // => "userProfileId"
```

### `pascalCase`

Normalizes free-form input into upper-initial `PascalCase`.

```ts
import { String } from "effect"

console.log(String.pascalCase("hello world")) // => "HelloWorld"
console.log(String.pascalCase("user-profile-id")) // => "UserProfileId"
```

### `snakeCase`

Normalizes free-form input into lowercase `snake_case`.

```ts
import { String } from "effect"

console.log(String.snakeCase("Hello World")) // => "hello_world"
console.log(String.snakeCase("userProfileId")) // => "user_profile_id"
```

### `kebabCase`

Normalizes free-form input into lowercase `kebab-case` — ideal for slugs.

```ts
import { String } from "effect"

console.log(String.kebabCase("User profile ID")) // => "user-profile-id"
console.log(String.kebabCase("helloWorld")) // => "hello-world"
```

### `constantCase`

Normalizes free-form input into uppercase, underscore-separated
`CONSTANT_CASE`.

```ts
import { String } from "effect"

console.log(String.constantCase("Hello World")) // => "HELLO_WORLD"
console.log(String.constantCase("userProfileId")) // => "USER_PROFILE_ID"
```

## RegExp helpers

The tiny `RegExp` module rounds out the string toolkit: the native constructor,
a guard, and — most usefully — `escape` for safely embedding untrusted literal
text inside a pattern.

### `RegExp`

The native `globalThis.RegExp` constructor, re-exported.

```ts
import { RegExp } from "effect"

const pattern = new RegExp.RegExp("hello", "i")
console.log(pattern.test("Hello World")) // => true
console.log(pattern.test("goodbye")) // => false
```

### `isRegExp`

A guard that narrows unknown input to `RegExp`.

```ts
import { RegExp } from "effect"

console.log(RegExp.isRegExp(/a/)) // => true
console.log(RegExp.isRegExp("a")) // => false
```

### `escape`

Escapes regular-expression metacharacters in a string so it matches literally.
Always run user-supplied text through `escape` before interpolating it into a
pattern — otherwise characters like `.` or `*` are interpreted as regex syntax.

```ts
import { RegExp } from "effect"

console.log(RegExp.escape("a*b")) // => "a\\*b"

// Build a literal matcher from untrusted input safely
const userInput = "a+b.txt"
const expression = new RegExp.RegExp(`^${RegExp.escape(userInput)}$`)
console.log(expression.test("a+b.txt")) // => true
console.log(expression.test("aaab.txt")) // => false
```

`escape` only escapes metacharacters — it does not add anchors, flags, or
grouping, so you remain in control of the surrounding pattern.