'TypeScript filter out nulls from an array
TypeScript, --strictNullChecks mode.
Suppose I have an array of nullable strings (string | null)[]. What would be a single-expression way to remove all nulls in a such a way that the result has type string[]?
const array: (string | null)[] = ["foo", "bar", null, "zoo", null];
const filterdArray: string[] = ???;
Array.filter does not work here:
// Type '(string | null)[]' is not assignable to type 'string[]'
array.filter(x => x != null);
Array comprehensions could've work but they are not supported by TypeScript.
Actually the question can be generalized to the problem of filtering an array of any union type by removing entries having one particular type from the union. But let's focus on unions with null and perhaps undefined as these are the most common usecases.
Solution 1:[1]
Similar to @bijou-trouvaille's answer, you just need to declare the <arg> is <Type> as the output of the filter function:
array.filter((x): x is MyType => x !== null);
Solution 2:[2]
One more for good measure as people often forget about flatMap which can handle filter and map in one go (this also doesn't require any casting to string[]):
// (string | null)[]
const arr = ["a", null, "b", "c"];
// string[]
const stringsOnly = arr.flatMap(f => f ? [f] : []);
Solution 3:[3]
One liner:
const filteredArray: string[] = array.filter((s): s is string => Boolean(s));
The trick is to pass a type predicate (:s is string syntax).
This answer shows that Array.filter requires users to provide a type predicate.
Solution 4:[4]
You can cast your filter result into the type you want:
const array: (string | null)[] = ["foo", "bar", null, "zoo", null];
const filterdArray = array.filter(x => x != null) as string[];
This works for the more general use case that you mentioned, for example:
const array2: (string | number)[] = ["str1", 1, "str2", 2];
const onlyStrings = array2.filter(x => typeof x === "string") as string[];
const onlyNumbers = array2.filter(x => typeof x === "number") as number[];
Solution 5:[5]
To avoid everybody having to write the same type guard helper functions over and over again I bundled functions called isPresent, isDefined and isFilled into a helper library: https://www.npmjs.com/package/ts-is-present
The type definitions are currently:
export declare function isPresent<T>(t: T | undefined | null): t is T;
export declare function isDefined<T>(t: T | undefined): t is T;
export declare function isFilled<T>(t: T | null): t is T;
You can use this like so:
import { isDefined } from 'ts-is-present';
type TestData = {
data: string;
};
const results: Array<TestData | undefined> = [
{ data: 'hello' },
undefined,
{ data: 'world' }
];
const definedResults: Array<TestData> = results.filter(isDefined);
console.log(definedResults);
When Typescript bundles this functionality in I'll remove the package. But, for now, enjoy.
Solution 6:[6]
Here is a solution that uses NonNullable. I find it even a little bit more concise than the accepted answer by @bijou-trouvaille
function notEmpty<TValue>(value: TValue): value is NonNullable<TValue> {
return value !== null && value !== undefined;
}
const array: (string | null | undefined)[] = ['foo', 'bar', null, 'zoo', undefined];
const filteredArray: string[] = array.filter(notEmpty);
console.log(filteredArray)
[LOG]: ["foo", "bar", "zoo"]
Solution 7:[7]
If you already use Lodash, you can use compact.
Or, if you prefer Ramda, the ramda-adjunct has also compact function.
Both have types, so your tsc will be happy and get the correct types as a result.
From Lodash d.ts file:
/**
* Creates an array with all falsey values removed. The values false, null, 0, "", undefined, and NaN are
* falsey.
*
* @param array The array to compact.
* @return Returns the new array of filtered values.
*/
compact<T>(array: List<T | null | undefined | false | "" | 0> | null | undefined): T[];
Solution 8:[8]
I believe you have it all good except that the type checking just makes the filtered type not be different than the return type.
const array: (string | null)[] = ["foo", "bar", null, "zoo", null];
const filterdArray: string[] = array.filter(f => f !== undefined && f !== null) as any;
console.log(filterdArray);
Solution 9:[9]
I think this will be an easy approach, with more cleaner code
const array: (string | null)[] = ['foo', 'bar', null, 'zoo', null];
const filteredArray: string[] = array.filter(a => !!a);
Solution 10:[10]
simply use
array.filter(Boolean);
This will work for all truth values.
This, unfortunately, do not provide type inference, found this solution on here
type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T; //from lodash
function truthy<T>(value: T): value is Truthy<T> {
return Boolean(value); // or !!value
}
const arr =["hello","felow","developer","",null,undefined];
const truthyArr = arr.filter(truthy);
// the type of truthyArr will be string[]
Solution 11:[11]
const filterdArray = array.filter(f => !!f) as string[];
Solution 12:[12]
If you are checking null with other conditions using filter simply this can be used hope this helps for some one who is looking solutions for an object array
array.filter(x => x != null);
array.filter(x => (x != null) && (x.name == 'Tom'));
Solution 13:[13]
TypeScript has some utilities to infer the type of the array and exclude the null values from it:
const arrayWithNulls = ["foo", "bar", null, "zoo", null]
type ArrayWithoutNulls = NonNullable<typeof arrayWithNulls[number]>[]
const arrayWithoutNulls = arrayWithNulls.filter(x => x != null) as ArrayWithoutNulls
Longer but safer than just manually casting as string[] on your new array.
Step by step:
- Get the types from the original array:
typeof arrayWithNulls[number] // => string | null
- Exclude the
nullvalues:
NonNullable<typeof arrayWithNulls[number]> // => string
- Make it an array:
NonNullable<typeof arrayWithNulls[number]>[] // => string[]
Links:
NonNullable(official doc)typeof array[number](blog post, I couldn't find anything about it on the official doc)
Solution 14:[14]
Using reduce
Some answers suggest reduce, here is how:
const languages = ["fr", "en", undefined, null, "", "de"]
// the one I prefer:
languages.reduce<string[]>((previous, current) => current ? [...previous, current] : previous, [])
// or
languages.reduce((previous, current) => current ? [...previous, current] : previous, Array<string>())
// or
const reducer = (previous: string[], current: string | undefined | null) => current ? [...previous, current] : previous
languages.reduce(reducer, [])
Result: ["fr", "en", "de"]
TS Playground here.
Solution 15:[15]
I've come back to this question many times hoping some new Typescript feature or typing may fix it.
Here's a simple trick I quite like for when combining map with a subsequent filter.
const animals = ['cat', 'dog', 'mouse', 'sheep'];
const notDogAnimals = animals.map(a =>
{
if (a == 'dog')
{
return null!; // just skip dog
}
else {
return { animal: a };
}
}).filter(a => a);
You'll see I'm returning null! which actually becomes type never - meaning that the final type doesn't have null.
This is a slight variation on the original question but I find myself in this scenario quite often and it helps avoid another method call. Hopefully someday Typescript will come up with a better way.
Solution 16:[16]
The shortest way:
const validData = array.filter(Boolean)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
