To remove empty strings from an array, filter them out: values.filter(Boolean) is the short version and values.filter(value => value !== "") is the explicit one. Both return a new array and leave the original alone. If the array also contains strings that are only spaces or tabs, trim first: values.filter(value => value.trim() !== ""). The code is the same in JavaScript and TypeScript; the one extra thing TypeScript needs is a type predicate, so the result is typed string[] rather than keeping null and undefined in the type. All of it is below, run and with the real output.
Every example was run with Node.js v22.22.2 and TypeScript 5.2.2 in the Windows Command Prompt. The JavaScript files run straight through node; the TypeScript ones are compiled with tsc first. Reference: Array.prototype.filter() on MDN.
Remove empty strings from an array
An empty string is falsy, so passing the Boolean function as the predicate keeps everything else. For an array that holds only strings, this is safe and is the version you will see most often:
const cities = ["Austin", "", "Denver", "", "Seattle"];
console.log(cities.filter(Boolean)); // shortest way
console.log(cities.filter(city => city !== "")); // the explicit test
console.log(cities.length, "->", cities.filter(Boolean).length);
console.log(cities); // the original is untouched
Output:
[ 'Austin', 'Denver', 'Seattle' ]
[ 'Austin', 'Denver', 'Seattle' ]
5 -> 3
[ 'Austin', '', 'Denver', '', 'Seattle' ]

| What you want to remove | Use |
|---|---|
| Empty strings only | values.filter(value => value !== "") |
| Empty strings, shortest form | values.filter(Boolean) |
| Empty and whitespace-only | values.filter(value => value.trim() !== "") |
| Empty, and trim what is left | values.map(v => v.trim()).filter(Boolean) |
Empty, null and undefined | values.filter(v => v?.trim()) |
| The same, keeping the type in TypeScript | values.filter((v): v is string => ...) |
Strings that are only spaces
" " is not an empty string: it has three characters in it, so it is truthy and survives filter(Boolean). Trim before you test, or trim and filter in one pass when you want the surviving values cleaned up too:
const input = ["Austin", "", " ", "Denver\t", "\n", "Seattle"];
// filter(Boolean) keeps " " because a space is not an empty string
console.log(input.filter(Boolean));
// trim() first, and the whitespace-only entries go too
console.log(input.filter(value => value.trim() !== ""));
// clean and filter in one pass
console.log(input.map(value => value.trim()).filter(Boolean));
Output:
[ 'Austin', ' ', 'Denver\t', '\n', 'Seattle' ]
[ 'Austin', 'Denver\t', 'Seattle' ]
[ 'Austin', 'Denver', 'Seattle' ]

" ", "\t" and "\n" as well.Where the empty strings come from: split()
Most empty strings in an array are made by split(). Two separators in a row produce an empty string between them, and a trailing separator produces one at the end. Splitting an empty string is the odd case: it gives you a one-item array holding an empty string, which is why the last line still needs the filter to end up empty. Cleaning a comma-separated list is the classic use:
const line = "Austin,,Denver, ,Seattle,";
console.log(line.split(",")); // this is where empty strings come from
console.log(line.split(",").map(v => v.trim()).filter(Boolean));
const tags = " react ,, typescript , ";
console.log(tags.split(",").map(t => t.trim()).filter(Boolean));
console.log("no separators".split(",").filter(Boolean));
console.log("".split(",").filter(Boolean)); // an empty string splits into [""]
Output:
[ 'Austin', '', 'Denver', ' ', 'Seattle', '' ]
[ 'Austin', 'Denver', 'Seattle' ]
[ 'react', 'typescript' ]
[ 'no separators' ]
[]
The pattern split then map(trim) then filter(Boolean) is worth remembering as a unit: it turns messy user input into a clean list in one line.
filter(Boolean) removes more than empty strings
On a mixed array, Boolean throws out every falsy value: 0, false, NaN, null and undefined go along with "". If 0 is real data, that line quietly loses it. Test explicitly when the array holds more than strings, and see filtering arrays for more on choosing a predicate:
const values = ["Austin", "", 0, "Denver", null, false, NaN, "0", undefined];
console.log(values.filter(Boolean)); // drops every falsy value, not only ""
console.log(values.filter(value => value !== "")); // keeps 0, null, false and NaN
// keep real values, drop empty strings and nothing else
console.log(values.filter(value => value !== "" && value !== null && value !== undefined));
Output:
[ 'Austin', 'Denver', '0' ]
[
'Austin', 0,
'Denver', null,
false, NaN,
'0', undefined
]
[ 'Austin', 0, 'Denver', false, NaN, '0' ]

"0" survives: it is a non-empty string, so it is truthy.Arrays of objects
With objects you either drop whole records that are missing a value, or keep the record and strip its empty fields. The second one is handy before sending a form payload to an API, and it pairs with filtering an array of objects:
const rows = [
{ id: "ORD-1001", customer: "Emma Johnson", note: "" },
{ id: "ORD-1002", customer: "", note: "call back" },
{ id: "ORD-1003", customer: "Olivia Davis", note: " " },
];
// drop the records whose customer name is missing
console.log(rows.filter(row => row.customer.trim() !== "").map(r => r.id));
// or keep every record and drop the empty fields inside it
const clean = rows.map(row =>
Object.fromEntries(Object.entries(row).filter(([, value]) => String(value).trim() !== ""))
);
console.log(clean);
Output:
[ 'ORD-1001', 'ORD-1003' ]
[
{ id: 'ORD-1001', customer: 'Emma Johnson' },
{ id: 'ORD-1002', note: 'call back' },
{ id: 'ORD-1003', customer: 'Olivia Davis' }
]
Cleaning rows from a file or a textarea
The same three calls scale up to a whole CSV or a pasted block of text: split into lines, clean each line, then drop the lines that are left with nothing. Counting what you removed gives you the message to show the user:
const csv = `Austin,TX,
,,
Denver, CO ,USA
`;
// clean every cell, then drop the rows that end up with nothing in them
const rows = csv
.split("\n")
.map(line => line.split(",").map(cell => cell.trim()).filter(Boolean))
.filter(row => row.length > 0);
console.log(rows);
// keep a count of what was thrown away, for a validation message
const cells = ["Austin", "", "Denver", " ", "Seattle"];
const kept = cells.filter(cell => cell.trim() !== "");
console.log(`kept ${kept.length} of ${cells.length}, removed ${cells.length - kept.length} empty cells`);
Output:
[ [ 'Austin', 'TX' ], [ 'Denver', 'CO', 'USA' ] ]
kept 3 of 5, removed 2 empty cells
TypeScript: keeping the result typed as string[]
Here is the one place where TypeScript needs more than the JavaScript version. Filtering does remove the empty values at runtime, but on TypeScript 5.2 the type does not change, so the result cannot be assigned to string[]:
const raw: (string | null | undefined)[] = ["Austin", "", null, "Denver", undefined];
const cities: string[] = raw.filter(Boolean); // still (string | null | undefined)[]
console.log(cities);
tsc output:
remove_empty_types.ts(3,7): error TS2322: Type '(string | null | undefined)[]' is not assignable to type 'string[]'.
Type 'string | null | undefined' is not assignable to type 'string'.
Type 'undefined' is not assignable to type 'string'.

A type predicate, value is string, fixes it, and wrapping it in a small isFilled helper means you write it once. TypeScript 5.5 and later infer this from the function body. The same approach removes undefined values from an array:
const raw: (string | null | undefined)[] = ["Austin", "", null, "Denver", undefined, " "];
// a type predicate keeps the result typed as string[]
const isFilled = (value: string | null | undefined): value is string =>
typeof value === "string" && value.trim() !== "";
const cities: string[] = raw.filter(isFilled);
console.log(cities, cities.map(city => city.toUpperCase()));
// a reusable version for any array of optional strings
function withoutEmpty(values: readonly (string | null | undefined)[]): string[] {
return values.filter(isFilled).map(value => value.trim());
}
console.log(withoutEmpty([" Austin ", "", null, " Denver"]));
Output:
[ 'Austin', 'Denver' ] [ 'AUSTIN', 'DENVER' ]
[ 'Austin', 'Denver' ]
