String
The string category identifies string, string literals and template literals, and provides a few
type-level string operations. The predicates follow the usual
type branching contract, so each one can act as a boolean check, as a
filter, or as a branch selector for your own types.
IsString and IsNotString
Section titled “IsString and IsNotString”type IsString<T, $O extends $StrictOptions<$O, IsString.$Options> = {}>type IsNotString<T, $O extends $StrictOptions<$O, IsNotString.$Options> = {}>IsString<T> is true for string and for any string literal.
type R1 = IsString<string> // truetype R2 = IsString<'a'> // truetype R3 = IsString<1> // falsetype R4 = IsString<string | boolean> // boolean$Options combines $Selection.Options, $Distributive.Options, $Exact.Options and input options for
any, unknown, never and void. That means selection: 'filter' returns the input instead of a
boolean, and distributive: false stops a union from being evaluated member by member:
type R1 = IsString<'a', { selection: 'filter' }> // 'a'type R2 = IsString<string | boolean, { selection: 'filter' }> // stringtype R3 = IsString<string | 1, { distributive: false }> // falseIsNotString<T> is the negation, and its filter selection keeps everything that is not a string:
type R1 = IsNotString<1> // truetype R2 = IsNotString<'a'> // falseSee Options for what selection, distributive and exact mean, and
type branching for $any, $unknown, $never, $void, $then,
$else and the IsString.$Branch selectors.
IsStringLiteral and IsNotStringLiteral
Section titled “IsStringLiteral and IsNotStringLiteral”type IsStringLiteral<T, $O extends $StrictOptions<$O, IsStringLiteral.$Options> = {}>type IsNotStringLiteral<T, $O extends $StrictOptions<$O, IsNotStringLiteral.$Options> = {}>Distinguishes a literal from the wide string type. Template literals count as literals by default.
type R1 = IsStringLiteral<'a'> // truetype R2 = IsStringLiteral<`${number}`> // truetype R3 = IsStringLiteral<string> // falseUse exact: true to reject template literals and accept only plain literals:
type R1 = IsStringLiteral<`${number}`> // truetype R2 = IsStringLiteral<`${number}`, { exact: true }> // falseIsTemplateLiteral and IsNotTemplateLiteral
Section titled “IsTemplateLiteral and IsNotTemplateLiteral”type IsTemplateLiteral<T, $O extends $StrictOptions<$O, IsTemplateLiteral.$Options> = {}>type IsNotTemplateLiteral<T, $O extends $StrictOptions<$O, IsNotTemplateLiteral.$Options> = {}>The mirror image of the above: only template literals pass.
type R1 = IsTemplateLiteral<`a${number}`> // truetype R2 = IsTemplateLiteral<'foo'> // falsetype R3 = IsTemplateLiteral<string> // false
type R4 = IsTemplateLiteral<`${number}`, { selection: 'filter' }> // `${number}`type R5 = IsTemplateLiteral<'a', { selection: 'filter' }> // neverStringIncludes
Section titled “StringIncludes”type StringIncludes<Subject extends string, Search extends string, Then = true, Else = false>Checks whether Subject contains Search. This type predates type branching and takes plain
Then/Else parameters instead of an options object.
type R1 = StringIncludes<'abc', 'a'> // truetype R2 = StringIncludes<'abc', 'd'> // falsetype R3 = StringIncludes<'abc', 'd', 'yes', 'no'> // 'no'StringSplit
Section titled “StringSplit”type StringSplit<Subject extends string, Separator extends string>Splits Subject on Separator and returns a tuple. An empty separator splits into characters.
type R1 = StringSplit<'abc', ''> // ['a', 'b', 'c']type R2 = StringSplit<'a.b.c', '.'> // ['a', 'b', 'c']type R3 = StringSplit<'abc', 'b'> // ['a', 'c']StringPlus namespace
Section titled “StringPlus namespace”StringPlus holds the type-level forms of the String.prototype methods, under names too generic
for the top level. Includes and Split are the same operations as StringIncludes and
StringSplit, under shorter names.
import type { StringPlus } from 'type-plus'
type R1 = StringPlus.Includes<'abc', 'a'> // truetype R2 = StringPlus.Split<'abc', ''> // ['a', 'b', 'c']StringPlus.Includes takes the type branching options object
rather than the positional Then/Else that StringIncludes still uses.
type R = StringPlus.Includes<'abc', 'd', { $then: 'yes'; $else: 'no' }> // 'no'type R = StringPlus.Includes<'abc', 'a', { selection: 'filter' }> // 'abc'type R = StringPlus.Includes<'abc', 'd', { selection: 'filter' }> // neverStartsWith and EndsWith
Section titled “StartsWith and EndsWith”namespace StringPlus { type StartsWith<Subject extends string, Search extends string, $O extends $StrictOptions<$O, StartsWith.$Options> = {}> type EndsWith<Subject extends string, Search extends string, $O extends $StrictOptions<$O, EndsWith.$Options> = {}>}The type-level String.prototype.startsWith and endsWith. They are predicates shaped like
StringPlus.Includes: an empty Search matches every string literal, and the wide string takes the
$else branch.
type R = StringPlus.StartsWith<'abc', 'ab'> // truetype R = StringPlus.StartsWith<'abc', 'bc'> // falsetype R = StringPlus.EndsWith<'abc', 'bc'> // truetype R = StringPlus.EndsWith<'abc', 'ab'> // false
type R = StringPlus.StartsWith<'abc' | 'bcd', 'ab', { selection: 'filter' }> // 'abc'type R = StringPlus.EndsWith<'abc', 'ab', { $then: 'yes'; $else: 'no' }> // 'no'Replace and ReplaceAll
Section titled “Replace and ReplaceAll”namespace StringPlus { type Replace<Subject extends string, Search extends string, Replacement extends string> type ReplaceAll<Subject extends string, Search extends string, Replacement extends string>}The type-level String.prototype.replace and replaceAll with a string pattern. Replace replaces
the first occurrence of Search, and ReplaceAll replaces every occurrence, left to right and
without overlap. A Subject without Search comes back unchanged. A wide Subject or Search
gives string.
type R = StringPlus.Replace<'a.b.c', '.', '/'> // 'a/b.c'type R = StringPlus.ReplaceAll<'a.b.c', '.', '/'> // 'a/b/c'type R = StringPlus.ReplaceAll<'aaa', 'aa', 'b'> // 'ba'type R = StringPlus.Replace<'abc', 'd', 'x'> // 'abc'An empty Search behaves as it does at runtime:
type R = StringPlus.Replace<'abc', '', 'x'> // 'xabc'type R = StringPlus.ReplaceAll<'abc', '', '-'> // '-a-b-c-'The inverse of StringPlus.Split is ArrayPlus.Join, because join is
an Array.prototype method.
type R = ArrayPlus.Join<StringPlus.Split<'a.b.c', '.'>, '.'> // 'a.b.c'ExtractManipulatedString
Section titled “ExtractManipulatedString”type ExtractManipulatedString<T extends string>A type util. It unwraps the intrinsic string manipulation types — Uppercase, Lowercase,
Capitalize and Uncapitalize — to recover the string being manipulated. IsStringLiteral uses it
to see through those wrappers.
It only sees a wrapper that TypeScript has not already resolved. Applied to a literal, the intrinsic evaluates first and there is nothing left to unwrap:
type R1 = ExtractManipulatedString<Uppercase<string>> // stringtype R2 = ExtractManipulatedString<Uppercase<'abc'>> // 'ABC'type R3 = ExtractManipulatedString<'abc'> // 'abc'Reference
Section titled “Reference”| Type | Description |
|---|---|
IsString<T, $O> |
T is string or a string literal |
IsNotString<T, $O> |
T is neither string nor a string literal |
IsStringLiteral<T, $O> |
T is a string literal (template literals included unless exact: true) |
IsNotStringLiteral<T, $O> |
T is not a string literal |
IsTemplateLiteral<T, $O> |
T is a template literal |
IsNotTemplateLiteral<T, $O> |
T is not a template literal |
StringIncludes<S, Search, Then, Else> |
S contains Search |
StringSplit<S, Separator> |
split S into a tuple |
StringPlus.Includes<S, Search, $O> / StringPlus.Split<S, Separator> |
namespaced aliases of the two above; Includes takes $O |
StringPlus.StartsWith<S, Search, $O> |
S starts with Search |
StringPlus.EndsWith<S, Search, $O> |
S ends with Search |
StringPlus.Replace<S, Search, Replacement> |
replace the first Search in S |
StringPlus.ReplaceAll<S, Search, Replacement> |
replace every Search in S |
ExtractManipulatedString<T> |
unwrap Uppercase/Lowercase/Capitalize/Uncapitalize |
Source: src/string.