How to Get the String After a Character in TypeScript

You can get the string after a character in TypeScript by finding the character with indexOf() and passing the next position to slice(). Check for -1 first, because a missing character would otherwise hand you back the whole string:

const i = email.indexOf("@");
const domain = i === -1 ? "" : email.slice(i + 1);   // "acmesupply.com"

Quick note on setup: strict mode was on for every compile, with TypeScript 7.0.2 and Node.js v22.22.2, and each output block below is pasted straight from that console.

I’ll cover the first occurrence, the last occurrence, the nth occurrence, what to do when the separator isn’t there, and when substring(), split() or a regular expression is the better fit.

Get the string after the first occurrence of a character

indexOf() returns the position of the first match. Add 1 to step past a single character, or add the separator’s length when it’s longer, like ": ". Calling slice() with only a start index runs to the end of the string:

const email: string = "jordan.miles@acmesupply.com";
const at = email.indexOf("@");
const domain = email.slice(at + 1);
console.log("domain:", domain);

const orderId: string = "ORD-2026-0418";
console.log("after first '-':", orderId.slice(orderId.indexOf("-") + 1));

// separator longer than one character: skip its full length
const header: string = "Ship-To: Austin, TX 78701";
const sep = ": ";
console.log("value:", header.slice(header.indexOf(sep) + sep.length));

Output:

domain: acmesupply.com
after first '-': 2026-0418
value: Austin, TX 78701
Command Prompt output of TypeScript indexOf and slice getting the domain after @, the text after the first dash and a header value
The domain after @, everything after the first dash, and a header value after a two-character separator.

Notice that ORD-2026-0418 keeps its second dash: only the first one counts. If you just need a yes or no before cutting, see how to check if a string contains a substring in TypeScript.

What happens when the character isn’t in the string?

This is the bug I see most often. indexOf() returns -1 for a missing character, so -1 + 1 becomes 0 and slice(0) quietly returns the entire string. A small helper that returns undefined makes the miss explicit:

const sku: string = "WIDGET42";
console.log("naive:", sku.slice(sku.indexOf("-") + 1));   // indexOf is -1

function afterFirst(text: string, sep: string): string | undefined {
  const i = text.indexOf(sep);
  return i === -1 ? undefined : text.slice(i + sep.length);
}

console.log(JSON.stringify(afterFirst("SKU-1001", "-")));
console.log(JSON.stringify(afterFirst("WIDGET42", "-")));
console.log(JSON.stringify(afterFirst("note:", ":")));     // separator at the end
console.log("with fallback:", afterFirst("WIDGET42", "-") ?? "(none)");

Output:

naive: WIDGET42
"1001"
undefined
""
with fallback: (none)

Keep the difference between undefined (no separator) and "" (separator at the very end) in mind. They mean different things, and ?? only replaces the first one.

The payoff of the string | undefined return type is that strict TypeScript won’t let you forget the missing case:

function afterFirst(text: string, sep: string): string | undefined {
  const i = text.indexOf(sep);
  return i === -1 ? undefined : text.slice(i + sep.length);
}

const code = afterFirst("SKU-1001", "-");
console.log(code.toUpperCase());

Output:

after_unchecked.ts(7,13): error TS18048: 'code' is possibly 'undefined'.
Command Prompt showing TypeScript error TS18048 'code' is possibly 'undefined' when using the result of an afterFirst helper
The compiler stops with TS18048 until you handle the undefined result.

Fix it with an if (code) check, optional chaining or a ?? fallback. For more on empty results, read my post on how to check if a string is empty in TypeScript.

Get the string after the last occurrence with lastIndexOf()

File paths, URLs and dates usually repeat the separator, and you want what follows the final one. Swap indexOf() for lastIndexOf(); nothing else changes:

const path: string = "reports/2026/q3/sales-summary.csv";
console.log("file:", path.slice(path.lastIndexOf("/") + 1));
console.log("extension:", path.slice(path.lastIndexOf(".") + 1));

const url: string = "https://shop.example.com/orders/2026/10/18/A7731";
console.log("last segment:", url.slice(url.lastIndexOf("/") + 1));

function afterLast(text: string, sep: string): string | undefined {
  const i = text.lastIndexOf(sep);
  return i === -1 ? undefined : text.slice(i + sep.length);
}

const date: string = "2026-10-18";
console.log("after first '-':", date.slice(date.indexOf("-") + 1));
console.log("after last '-': ", afterLast(date, "-"));
console.log("no dot:", JSON.stringify(afterLast("README", ".")));
console.log("trailing slash:", JSON.stringify(afterLast("orders/2026/", "/")));

Output:

file: sales-summary.csv
extension: csv
last segment: A7731
after first '-': 10-18
after last '-':  18
no dot: undefined
trailing slash: ""
Command Prompt output of TypeScript lastIndexOf and slice returning a file name, extension, URL segment and missing-separator results
The file name, extension and last URL segment, then first versus last dash, a missing dot and a trailing slash.

A trailing slash gives an empty string, so trim it first if your URLs may end with one. If you prefer an array-based one-liner, I cover split().at(-1) in how to split a string and get the last element in TypeScript.

Get the string after the nth occurrence of a separator

indexOf() takes an optional second argument, the position to start searching from. Call it in a loop, each time starting just past the previous hit, and you can stop at any occurrence you like:

function afterNth(text: string, sep: string, n: number): string | undefined {
  let i = -sep.length;
  for (let k = 0; k < n; k++) {
    i = text.indexOf(sep, i + sep.length);   // search again past the last hit
    if (i === -1) return undefined;
  }
  return text.slice(i + sep.length);
}

const record: string = "Dallas|TX|75201|2026-10-18|$1249.99";
console.log("after 1st |:", afterNth(record, "|", 1));
console.log("after 2nd |:", afterNth(record, "|", 2));
console.log("after 4th |:", afterNth(record, "|", 4));
console.log("after 9th |:", afterNth(record, "|", 9));

Output:

after 1st |: TX|75201|2026-10-18|$1249.99
after 2nd |: 75201|2026-10-18|$1249.99
after 4th |: $1249.99
after 9th |: undefined

This works on pipe-delimited exports, log lines and anything else with a fixed field order. It returns undefined when there aren’t enough separators, matching the other helpers.

The last field still carries its dollar sign. Strip it before doing math, then follow my guide to convert a string to a number in TypeScript.

slice() vs substring() for the text after a character

For the normal case, slice(i + 1) and substring(i + 1) return the same thing. They differ with negative or reversed arguments, and that’s where bugs hide:

const city: string = "Denver, CO";
const i = city.indexOf(", ");

console.log("slice(i + 2):     ", city.slice(i + 2));
console.log("substring(i + 2): ", city.substring(i + 2));
console.log("slice(-2):        ", city.slice(-2));
console.log("substring(-2):    ", city.substring(-2));
console.log("slice(8, 3):      ", JSON.stringify(city.slice(8, 3)));
console.log("substring(8, 3):  ", JSON.stringify(city.substring(8, 3)));

Output:

slice(i + 2):      CO
substring(i + 2):  CO
slice(-2):         CO
substring(-2):     Denver, CO
slice(8, 3):       ""
substring(8, 3):   "ver, "

slice(-2) counts from the end, while substring() treats negatives as 0 and swaps a start that’s bigger than the end. I use slice() everywhere and avoid the legacy substr(), which MDN marks as deprecated.

Once you have the piece, you may want to clean it up; see how to remove spaces from a string in TypeScript.

Can you use split() to get the string after a character?

You can, but read the output carefully. split("=")[1] only returns the text up to the next separator, and the limit argument doesn’t keep the remainder either; it just throws extra pieces away:

const line: string = "note=Ship by 10/18=priority";

console.log("split()[1]:   ", line.split("=")[1]);
console.log("split(sep, 2):", line.split("=", 2));

const [key, ...rest] = line.split("=");
console.log("rest joined:  ", key, "->", rest.join("="));

const plain: string = "standalone";
const second: string = plain.split("=")[1];      // typed string, but...
console.log("missing sep:  ", second);

Output:

split()[1]:    Ship by 10/18
split(sep, 2): [ 'note', 'Ship by 10/18' ]
rest joined:   note -> Ship by 10/18=priority
missing sep:   undefined
Command Prompt output of TypeScript split showing index 1 and split with a limit both losing text after a second equals sign
Index [1] and split(sep, 2) lose =priority; rest-and-join keeps it, and a missing separator gives undefined.

The last line is sneaky: TypeScript types split()[1] as string, yet it’s undefined at runtime unless you enable noUncheckedIndexedAccess. For comma lists and other cases where you want every piece, see splitting a string by comma in TypeScript.

Get the text after a character with a regular expression

A regex pays off when the separator follows a pattern, such as a label with optional spaces, or when you want to capture a value by name. match() returns null on no match, so read it with optional chaining:

const log: string = "2026-10-18 09:42:15 ERROR: Payment declined for order A7731";
const m = log.match(/ERROR:\s*(.*)$/);
console.log("captured:    ", m?.[1]);

const email: string = "jordan.miles@acmesupply.com";
console.log("replace:     ", email.replace(/^[^@]*@/, ""));

const path: string = "reports/2026/q3/sales-summary.csv";
console.log("greedy last: ", path.replace(/^.*\//, ""));

const invoice: string = "Invoice INV-2026-0918 total: $1249.99";
const named = invoice.match(/total:\s*\$(?<amount>[\d.]+)/);
console.log("amount:      ", named?.groups?.amount);

console.log("no match:    ", "WIDGET42".match(/-(.*)$/));
console.log("replace miss:", "WIDGET42".replace(/^[^-]*-/, ""));

Output:

captured:     Payment declined for order A7731
replace:      acmesupply.com
greedy last:  sales-summary.csv
amount:       1249.99
no match:     null
replace miss: WIDGET42

replace(/^[^@]*@/, "") removes everything up to the first @, while the greedy /^.*\// runs to the last slash. When the pattern doesn’t match, replace() returns the input unchanged, the same trap as the plain indexOf version.

Regex replacement has more tricks; I go through them in replacing all occurrences of a substring in TypeScript. For simple separators, indexOf plus slice is faster to read and to run.

Methods to get the string after a character compared

MethodGets text afterIf the character is missing
slice(s.indexOf(c) + 1)First occurrenceWhole string (check -1)
slice(s.lastIndexOf(c) + 1)Last occurrenceWhole string (check -1)
afterFirst / afterLast helperFirst or last, any lengthundefined
indexOf(c, from) in a loopNth occurrenceundefined
split(c) + rest/joinFirst occurrenceEmpty string after join
match(/c(.*)$/)Pattern-basednull

Pick the helper version for anything user-facing, since it can’t confuse a missing separator with real data. After extracting a value, you’ll often check it against something else; I explain the options in how to compare strings in TypeScript.

Working with strings a lot this week? These guides pair well with this one:

Frequently asked questions

How do I get the string after a specific character in TypeScript?

Find it with const i = text.indexOf(char), then call text.slice(i + 1). Check i === -1 first so a missing character doesn’t return the whole string.

How do I get the text after the last occurrence of a character?

Use lastIndexOf() instead of indexOf(): path.slice(path.lastIndexOf("/") + 1) returns the file name from a path.

Why does my code return the whole string when the character is missing?

indexOf() returns -1, and slice(-1 + 1) is slice(0). Test for -1 and return undefined or a default instead.

Should I use slice() or substring()?

Both work for i + 1. Prefer slice(): it supports negative indexes and doesn’t silently swap reversed arguments like substring() does.

How do I get the string after a multi-character separator?

Add the separator’s length instead of 1: text.slice(text.indexOf(sep) + sep.length).

Where are slice() and indexOf() documented?

MDN’s String.prototype.slice() page covers start and end indexes and links to indexOf() and lastIndexOf().